diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js
index a5c086fc44..89d4094856 100644
--- a/examples/defaultScripts.js
+++ b/examples/defaultScripts.js
@@ -12,7 +12,6 @@ Script.load("progress.js");
Script.load("edit.js");
Script.load("selectAudioDevice.js");
Script.load("controllers/hydra/hydraMove.js");
-Script.load("headMove.js");
Script.load("inspect.js");
Script.load("lobby.js");
Script.load("notifications.js");
diff --git a/examples/dice.js b/examples/dice.js
index 553a233a25..ee48d59617 100644
--- a/examples/dice.js
+++ b/examples/dice.js
@@ -13,7 +13,8 @@
//
var isDice = false;
-var NUMBER_OF_DICE = 2;
+var NUMBER_OF_DICE = 4;
+var LIFETIME = 10000; // Dice will live for about 3 hours
var dice = [];
var DIE_SIZE = 0.20;
@@ -50,7 +51,7 @@ var diceButton = Overlays.addOverlay("image", {
});
var GRAVITY = -3.5;
-var LIFETIME = 300;
+
// NOTE: angularVelocity is in radians/sec
var MAX_ANGULAR_SPEED = Math.PI;
@@ -105,6 +106,7 @@ function mousePressEvent(event) {
var clickedText = false;
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
if (clickedOverlay == offButton) {
+ deleteDice();
Script.stop();
} else if (clickedOverlay == diceButton) {
var HOW_HARD = 2.0;
@@ -116,10 +118,8 @@ function mousePressEvent(event) {
}
function scriptEnding() {
- deleteDice();
Overlays.deleteOverlay(offButton);
Overlays.deleteOverlay(diceButton);
-
}
Entities.entityCollisionWithEntity.connect(entityCollisionWithEntity);
diff --git a/examples/harmonicOscillator.js b/examples/harmonicOscillator.js
new file mode 100644
index 0000000000..0ffbce8beb
--- /dev/null
+++ b/examples/harmonicOscillator.js
@@ -0,0 +1,65 @@
+// harmonicOscillator.js
+//
+// Created by Philip Rosedale on May 5, 2015
+// Copyright 2015 High Fidelity, Inc.
+//
+// An object moves around the edge of a disc while
+// changing color. The script is continuously updating
+// position, velocity, rotation, and color. The movement
+// should appear perfectly smooth to someone else,
+// provided their network connection is good.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+var ball, disc;
+var time = 0.0;
+var range = 1.0;
+var speed = 0.5;
+
+
+var basePosition = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
+
+ball = Entities.addEntity(
+ { type: "Box",
+ position: basePosition,
+ dimensions: { x: 0.1, y: 0.1, z: 0.1 },
+ color: { red: 255, green: 0, blue: 255 }
+ });
+
+disc = Entities.addEntity(
+ { type: "Sphere",
+ position: basePosition,
+ dimensions: { x: range, y: range / 20.0, z: range },
+ color: { red: 128, green: 128, blue: 128 }
+ });
+
+function update(deltaTime) {
+ time += deltaTime * speed;
+ if (!ball.isKnownID) {
+ ball = Entities.identifyEntity(ball);
+ }
+ rotation = Quat.angleAxis(time/Math.PI * 180.0, { x: 0, y: 1, z: 0 });
+ Entities.editEntity(ball,
+ {
+ color: { red: 255 * (Math.sin(time)/2.0 + 0.5),
+ green: 255 - 255 * (Math.sin(time)/2.0 + 0.5),
+ blue: 0 },
+ position: { x: basePosition.x + Math.sin(time) / 2.0 * range,
+ y: basePosition.y,
+ z: basePosition.z + Math.cos(time) / 2.0 * range },
+ velocity: { x: Math.cos(time)/2.0 * range,
+ y: 0.0,
+ z: -Math.sin(time)/2.0 * range },
+ rotation: rotation
+ });
+}
+
+function scriptEnding() {
+ Entities.deleteEntity(ball);
+ Entities.deleteEntity(disc);
+}
+
+Script.scriptEnding.connect(scriptEnding);
+Script.update.connect(update);
diff --git a/interface/resources/images/face-mute.svg b/interface/resources/images/face-mute.svg
new file mode 100644
index 0000000000..b16b7383c5
--- /dev/null
+++ b/interface/resources/images/face-mute.svg
@@ -0,0 +1,32 @@
+
+
\ No newline at end of file
diff --git a/interface/resources/images/face.svg b/interface/resources/images/face.svg
new file mode 100644
index 0000000000..62ce0deb25
--- /dev/null
+++ b/interface/resources/images/face.svg
@@ -0,0 +1,31 @@
+
+
\ No newline at end of file
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index e20ee73877..069eedd9e4 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -103,6 +103,7 @@
#include "audio/AudioIOStatsRenderer.h"
#include "audio/AudioScope.h"
+#include "devices/CameraToolBox.h"
#include "devices/DdeFaceTracker.h"
#include "devices/Faceshift.h"
#include "devices/Leapmotion.h"
@@ -266,6 +267,7 @@ bool setupEssentials(int& argc, char** argv) {
auto ddeFaceTracker = DependencyManager::set();
auto modelBlender = DependencyManager::set();
auto audioToolBox = DependencyManager::set();
+ auto cameraToolBox = DependencyManager::set();
auto avatarManager = DependencyManager::set();
auto lodManager = DependencyManager::set();
auto jsConsole = DependencyManager::set();
@@ -590,6 +592,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
// The offscreen UI needs to intercept the mouse and keyboard
// events coming from the onscreen window
_glWidget->installEventFilter(DependencyManager::get().data());
+
+ // initialize our face trackers after loading the menu settings
+ auto faceshiftTracker = DependencyManager::get();
+ faceshiftTracker->init();
+ connect(faceshiftTracker.data(), &FaceTracker::muteToggled, this, &Application::faceTrackerMuteToggled);
+ auto ddeTracker = DependencyManager::get();
+ ddeTracker->init();
+ connect(ddeTracker.data(), &FaceTracker::muteToggled, this, &Application::faceTrackerMuteToggled);
}
@@ -921,6 +931,14 @@ void Application::audioMuteToggled() {
muteAction->setChecked(DependencyManager::get()->isMuted());
}
+void Application::faceTrackerMuteToggled() {
+ QAction* muteAction = Menu::getInstance()->getActionForOption(MenuOption::MuteFaceTracking);
+ Q_CHECK_PTR(muteAction);
+ bool isMuted = getSelectedFaceTracker()->isMuted();
+ muteAction->setChecked(isMuted);
+ getSelectedFaceTracker()->setEnabled(!isMuted);
+}
+
void Application::aboutApp() {
InfoView::forcedShow(INFO_HELP_PATH);
}
@@ -1011,6 +1029,9 @@ bool Application::event(QEvent* event) {
case QEvent::MouseButtonPress:
mousePressEvent((QMouseEvent*)event);
return true;
+ case QEvent::MouseButtonDblClick:
+ mouseDoublePressEvent((QMouseEvent*)event);
+ return true;
case QEvent::MouseButtonRelease:
mouseReleaseEvent((QMouseEvent*)event);
return true;
@@ -1432,7 +1453,12 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
// stop propagation
return;
}
-
+
+ if (DependencyManager::get()->mousePressEvent(getMouseX(), getMouseY())) {
+ // stop propagation
+ return;
+ }
+
if (_rearMirrorTools->mousePressEvent(getMouseX(), getMouseY())) {
// stop propagation
return;
@@ -1450,6 +1476,24 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
}
}
+void Application::mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID) {
+ // if one of our scripts have asked to capture this event, then stop processing it
+ if (_controllerScriptingInterface.isMouseCaptured()) {
+ return;
+ }
+
+ if (activeWindow() == _window) {
+ if (event->button() == Qt::LeftButton) {
+ if (mouseOnScreen()) {
+ if (DependencyManager::get()->mouseDoublePressEvent(getMouseX(), getMouseY())) {
+ // stop propagation
+ return;
+ }
+ }
+ }
+ }
+}
+
void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
if (!_aboutToQuit) {
@@ -1845,18 +1889,45 @@ FaceTracker* Application::getActiveFaceTracker() {
(faceshift->isActive() ? static_cast(faceshift.data()) : NULL));
}
-void Application::setActiveFaceTracker() {
+FaceTracker* Application::getSelectedFaceTracker() {
+ FaceTracker* faceTracker = NULL;
#ifdef HAVE_FACESHIFT
- DependencyManager::get()->setTCPEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift));
+ if (Menu::getInstance()->isOptionChecked(MenuOption::Faceshift)) {
+ faceTracker = DependencyManager::get().data();
+ }
+#endif
+#ifdef HAVE_DDE
+ if (Menu::getInstance()->isOptionChecked(MenuOption::UseCamera)) {
+ faceTracker = DependencyManager::get().data();
+ }
+#endif
+ return faceTracker;
+}
+
+void Application::setActiveFaceTracker() {
+ bool isMuted = Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking);
+#ifdef HAVE_FACESHIFT
+ auto faceshiftTracker = DependencyManager::get();
+ faceshiftTracker->setIsMuted(isMuted);
+ faceshiftTracker->setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift) && !isMuted);
#endif
#ifdef HAVE_DDE
bool isUsingDDE = Menu::getInstance()->isOptionChecked(MenuOption::UseCamera);
Menu::getInstance()->getActionForOption(MenuOption::UseAudioForMouth)->setVisible(isUsingDDE);
Menu::getInstance()->getActionForOption(MenuOption::VelocityFilter)->setVisible(isUsingDDE);
- DependencyManager::get()->setEnabled(isUsingDDE);
+ auto ddeTracker = DependencyManager::get();
+ ddeTracker->setIsMuted(isMuted);
+ ddeTracker->setEnabled(isUsingDDE && !isMuted);
#endif
}
+void Application::toggleFaceTrackerMute() {
+ FaceTracker* faceTracker = getSelectedFaceTracker();
+ if (faceTracker) {
+ faceTracker->toggleMute();
+ }
+}
+
bool Application::exportEntities(const QString& filename, const QVector& entityIDs) {
QVector entities;
@@ -2025,10 +2096,6 @@ void Application::init() {
SixenseManager::getInstance().toggleSixense(true);
#endif
- // initialize our face trackers after loading the menu settings
- DependencyManager::get()->init();
- DependencyManager::get()->init();
-
Leapmotion::init();
RealSense::init();
@@ -2166,7 +2233,7 @@ void Application::updateMyAvatarLookAtPosition() {
isLookingAtSomeone = true;
// If I am looking at someone else, look directly at one of their eyes
- if (tracker) {
+ if (tracker && !tracker->isMuted()) {
// If a face tracker is active, look at the eye for the side my gaze is biased toward
if (tracker->getEstimatedEyeYaw() > _myAvatar->getHead()->getFinalYaw()) {
// Look at their right eye
@@ -2192,7 +2259,7 @@ void Application::updateMyAvatarLookAtPosition() {
//
// Deflect the eyes a bit to match the detected Gaze from 3D camera if active
//
- if (tracker) {
+ if (tracker && !tracker->isMuted()) {
float eyePitch = tracker->getEstimatedEyePitch();
float eyeYaw = tracker->getEstimatedEyeYaw();
const float GAZE_DEFLECTION_REDUCTION_DURING_EYE_CONTACT = 0.1f;
@@ -2247,7 +2314,7 @@ void Application::updateCamera(float deltaTime) {
if (!OculusManager::isConnected() && !TV3DManager::isConnected() &&
Menu::getInstance()->isOptionChecked(MenuOption::OffAxisProjection)) {
FaceTracker* tracker = getActiveFaceTracker();
- if (tracker) {
+ if (tracker && !tracker->isMuted()) {
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;
@@ -2311,7 +2378,7 @@ void Application::update(float deltaTime) {
PerformanceTimer perfTimer("devices");
DeviceTracker::updateAll();
FaceTracker* tracker = getActiveFaceTracker();
- if (tracker) {
+ if (tracker && !tracker->isMuted()) {
tracker->update(deltaTime);
}
SixenseManager::getInstance().update(deltaTime);
@@ -3241,6 +3308,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
{
PerformanceTimer perfTimer("3dOverlaysFront");
glClear(GL_DEPTH_BUFFER_BIT);
+ Glower glower; // Sets alpha to 1.0
_overlays.renderWorld(true);
}
activeRenderingThread = nullptr;
diff --git a/interface/src/Application.h b/interface/src/Application.h
index b432c581e4..58e49159a2 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -169,6 +169,7 @@ public:
void mouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0);
void mousePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
+ void mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
void mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0);
void touchBeginEvent(QTouchEvent* event);
@@ -218,6 +219,7 @@ public:
bool getLastMouseMoveWasSimulated() const { return _lastMouseMoveWasSimulated; }
FaceTracker* getActiveFaceTracker();
+ FaceTracker* getSelectedFaceTracker();
QSystemTrayIcon* getTrayIcon() { return _trayIcon; }
ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; }
@@ -396,6 +398,7 @@ public slots:
void resetSensors();
void setActiveFaceTracker();
+ void toggleFaceTrackerMute();
void aboutApp();
void showEditEntitiesHelp();
@@ -437,6 +440,7 @@ private slots:
void runTests();
void audioMuteToggled();
+ void faceTrackerMuteToggled();
void setCursorVisible(bool visible);
diff --git a/interface/src/GLCanvas.cpp b/interface/src/GLCanvas.cpp
index 9a9512a0b0..e169c96fe1 100644
--- a/interface/src/GLCanvas.cpp
+++ b/interface/src/GLCanvas.cpp
@@ -131,6 +131,7 @@ bool GLCanvas::event(QEvent* event) {
case QEvent::MouseMove:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::FocusIn:
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index 8070bd5555..b6940566ad 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -394,6 +394,12 @@ Menu::Menu() {
QAction* ddeFiltering = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::VelocityFilter, 0, true);
ddeFiltering->setVisible(false);
#endif
+#if defined(HAVE_FACESHIFT) || defined(HAVE_DDE)
+ faceTrackingMenu->addSeparator();
+ addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::MuteFaceTracking,
+ 0, false,
+ qApp, SLOT(toggleFaceTrackerMute()));
+#endif
auto avatarManager = DependencyManager::get();
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false,
diff --git a/interface/src/Menu.h b/interface/src/Menu.h
index 7d105687ab..a622668742 100644
--- a/interface/src/Menu.h
+++ b/interface/src/Menu.h
@@ -211,6 +211,7 @@ namespace MenuOption {
const QString Mirror = "Mirror";
const QString MuteAudio = "Mute Microphone";
const QString MuteEnvironment = "Mute Environment";
+ const QString MuteFaceTracking = "Mute Face Tracking";
const QString NoFaceTracking = "None";
const QString OctreeStats = "Entity Statistics";
const QString OffAxisProjection = "Off-Axis Projection";
diff --git a/interface/src/audio/AudioToolBox.cpp b/interface/src/audio/AudioToolBox.cpp
index 330e7bc194..e37c154429 100644
--- a/interface/src/audio/AudioToolBox.cpp
+++ b/interface/src/audio/AudioToolBox.cpp
@@ -35,7 +35,7 @@ bool AudioToolBox::mousePressEvent(int x, int y) {
return false;
}
-void AudioToolBox::render(int x, int y, bool boxed) {
+void AudioToolBox::render(int x, int y, int padding, bool boxed) {
glEnable(GL_TEXTURE_2D);
auto glCanvas = Application::getInstance()->getGLWidget();
@@ -79,7 +79,7 @@ void AudioToolBox::render(int x, int y, bool boxed) {
float iconColor = 1.0f;
- _iconBounds = QRect(x, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE);
+ _iconBounds = QRect(x + padding, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE);
if (!audioIO->isMuted()) {
glBindTexture(GL_TEXTURE_2D, _micTextureId);
iconColor = 1.0f;
diff --git a/interface/src/audio/AudioToolBox.h b/interface/src/audio/AudioToolBox.h
index 526de89b9c..7fbbab8fad 100644
--- a/interface/src/audio/AudioToolBox.h
+++ b/interface/src/audio/AudioToolBox.h
@@ -18,7 +18,7 @@
class AudioToolBox : public Dependency {
SINGLETON_DEPENDENCY
public:
- void render(int x, int y, bool boxed);
+ void render(int x, int y, int padding, bool boxed);
bool mousePressEvent(int x, int y);
protected:
diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp
index 41c2e9b54c..16cd906133 100644
--- a/interface/src/avatar/Head.cpp
+++ b/interface/src/avatar/Head.cpp
@@ -90,7 +90,7 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
// Only use face trackers when not playing back a recording.
if (!myAvatar->isPlaying()) {
FaceTracker* faceTracker = Application::getInstance()->getActiveFaceTracker();
- _isFaceTrackerConnected = faceTracker != NULL;
+ _isFaceTrackerConnected = faceTracker != NULL && !faceTracker->isMuted();
if (_isFaceTrackerConnected) {
_blendshapeCoefficients = faceTracker->getBlendshapeCoefficients();
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 557d630ebf..73c2e6a6e8 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -243,7 +243,7 @@ void MyAvatar::updateFromTrackers(float deltaTime) {
estimatedPosition /= OCULUS_LEAN_SCALE;
} else {
FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker();
- if (tracker) {
+ if (tracker && !tracker->isMuted()) {
estimatedPosition = tracker->getHeadTranslation();
_trackedHeadPosition = estimatedPosition;
estimatedRotation = glm::degrees(safeEulerAngles(tracker->getHeadRotation()));
diff --git a/interface/src/devices/CameraToolBox.cpp b/interface/src/devices/CameraToolBox.cpp
new file mode 100644
index 0000000000..26aff4bf9a
--- /dev/null
+++ b/interface/src/devices/CameraToolBox.cpp
@@ -0,0 +1,121 @@
+//
+// CameraToolBox.cpp
+// interface/src/devices
+//
+// Created by David Rowe on 30 Apr 2015.
+// Copyright 2015 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 "InterfaceConfig.h"
+
+#include
+#include
+
+#include "Application.h"
+#include "CameraToolBox.h"
+#include "FaceTracker.h"
+
+
+CameraToolBox::CameraToolBox() :
+ _iconPulseTimeReference(usecTimestampNow()),
+ _doubleClickTimer(NULL)
+{
+}
+
+CameraToolBox::~CameraToolBox() {
+ if (_doubleClickTimer) {
+ _doubleClickTimer->stop();
+ delete _doubleClickTimer;
+ }
+}
+
+bool CameraToolBox::mousePressEvent(int x, int y) {
+ if (_iconBounds.contains(x, y)) {
+ if (!_doubleClickTimer) {
+ // Toggle mute after waiting to check that it's not a double-click.
+ const int DOUBLE_CLICK_WAIT = 200; // ms
+ _doubleClickTimer = new QTimer(this);
+ connect(_doubleClickTimer, SIGNAL(timeout()), this, SLOT(toggleMute()));
+ _doubleClickTimer->setSingleShot(true);
+ _doubleClickTimer->setInterval(DOUBLE_CLICK_WAIT);
+ _doubleClickTimer->start();
+ }
+ return true;
+ }
+ return false;
+}
+
+bool CameraToolBox::mouseDoublePressEvent(int x, int y) {
+ if (_iconBounds.contains(x, y)) {
+ if (_doubleClickTimer) {
+ _doubleClickTimer->stop();
+ delete _doubleClickTimer;
+ _doubleClickTimer = NULL;
+ }
+ Application::getInstance()->resetSensors();
+ return true;
+ }
+ return false;
+}
+
+void CameraToolBox::toggleMute() {
+ delete _doubleClickTimer;
+ _doubleClickTimer = NULL;
+
+ FaceTracker* faceTracker = Application::getInstance()->getSelectedFaceTracker();
+ if (faceTracker) {
+ faceTracker->toggleMute();
+ }
+}
+
+void CameraToolBox::render(int x, int y, bool boxed) {
+ glEnable(GL_TEXTURE_2D);
+
+ auto glCanvas = Application::getInstance()->getGLWidget();
+ if (_enabledTextureId == 0) {
+ _enabledTextureId = glCanvas->bindTexture(QImage(PathUtils::resourcesPath() + "images/face.svg"));
+ }
+ if (_mutedTextureId == 0) {
+ _mutedTextureId = glCanvas->bindTexture(QImage(PathUtils::resourcesPath() + "images/face-mute.svg"));
+ }
+
+ const int MUTE_ICON_SIZE = 24;
+ _iconBounds = QRect(x, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE);
+ float iconColor = 1.0f;
+ if (!Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)) {
+ glBindTexture(GL_TEXTURE_2D, _enabledTextureId);
+ } else {
+ glBindTexture(GL_TEXTURE_2D, _mutedTextureId);
+
+ // Make muted icon pulsate
+ static const float PULSE_MIN = 0.4f;
+ static const float PULSE_MAX = 1.0f;
+ static const float PULSE_FREQUENCY = 1.0f; // in Hz
+ qint64 now = usecTimestampNow();
+ if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) {
+ // Prevents t from getting too big, which would diminish glm::cos precision
+ _iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND);
+ }
+ float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND;
+ float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f;
+ iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor;
+ }
+
+ glm::vec4 quadColor(iconColor, iconColor, iconColor, 1.0f);
+
+ glm::vec2 topLeft(_iconBounds.left(), _iconBounds.top());
+ glm::vec2 bottomRight(_iconBounds.right(), _iconBounds.bottom());
+ glm::vec2 texCoordTopLeft(1,1);
+ glm::vec2 texCoordBottomRight(0,0);
+
+ if (_boxQuadID == GeometryCache::UNKNOWN_ID) {
+ _boxQuadID = DependencyManager::get()->allocateID();
+ }
+
+ DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor, _boxQuadID);
+
+ glDisable(GL_TEXTURE_2D);
+}
\ No newline at end of file
diff --git a/interface/src/devices/CameraToolBox.h b/interface/src/devices/CameraToolBox.h
new file mode 100644
index 0000000000..5f9241c81d
--- /dev/null
+++ b/interface/src/devices/CameraToolBox.h
@@ -0,0 +1,45 @@
+//
+// CameraToolBox.h
+// interface/src/devices
+//
+// Created by David Rowe on 30 Apr 2015.
+// Copyright 2015 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_CameraToolBox_h
+#define hifi_CameraToolBox_h
+
+#include
+
+#include
+#include
+
+class CameraToolBox : public QObject, public Dependency {
+ Q_OBJECT
+ SINGLETON_DEPENDENCY
+
+public:
+ void render(int x, int y, bool boxed);
+ bool mousePressEvent(int x, int y);
+ bool mouseDoublePressEvent(int x, int y);
+
+protected:
+ CameraToolBox();
+ ~CameraToolBox();
+
+private slots:
+ void toggleMute();
+
+private:
+ GLuint _enabledTextureId = 0;
+ GLuint _mutedTextureId = 0;
+ int _boxQuadID = GeometryCache::UNKNOWN_ID;
+ QRect _iconBounds;
+ qint64 _iconPulseTimeReference = 0;
+ QTimer* _doubleClickTimer;
+};
+
+#endif // hifi_CameraToolBox_h
\ No newline at end of file
diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp
index 98f1636a32..59d2b42a51 100644
--- a/interface/src/devices/DdeFaceTracker.cpp
+++ b/interface/src/devices/DdeFaceTracker.cpp
@@ -178,9 +178,7 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 serverPort, qui
_filteredBrowUp(0.0f),
_lastEyeBlinks(),
_filteredEyeBlinks(),
- _lastEyeCoefficients(),
- _isCalculatingFPS(false),
- _frameCount(0)
+ _lastEyeCoefficients()
{
_coefficients.resize(NUM_FACESHIFT_BLENDSHAPES);
@@ -203,7 +201,16 @@ DdeFaceTracker::~DdeFaceTracker() {
#pragma warning(default:4351)
#endif
+void DdeFaceTracker::init() {
+ FaceTracker::init();
+ setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::UseCamera) && !_isMuted);
+}
+
void DdeFaceTracker::setEnabled(bool enabled) {
+ if (!_isInitialized) {
+ // Don't enable until have explicitly initialized
+ return;
+ }
#ifdef HAVE_DDE
// isOpen() does not work as one might expect on QUdpSocket; don't test isOpen() before closing socket.
_udpSocket.close();
@@ -314,7 +321,9 @@ float DdeFaceTracker::getBlendshapeCoefficient(int index) const {
}
void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
- if(buffer.size() > MIN_PACKET_SIZE) {
+ _lastReceiveTimestamp = usecTimestampNow();
+
+ if (buffer.size() > MIN_PACKET_SIZE) {
bool isFiltering = Menu::getInstance()->isOptionChecked(MenuOption::VelocityFilter);
Packet packet;
@@ -326,7 +335,7 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
memcpy(&translation, packet.translation, sizeof(packet.translation));
glm::quat rotation;
memcpy(&rotation, &packet.rotation, sizeof(packet.rotation));
- if (_reset || (_lastReceiveTimestamp == 0)) {
+ if (_reset || (_lastMessageReceived == 0)) {
memcpy(&_referenceTranslation, &translation, sizeof(glm::vec3));
memcpy(&_referenceRotation, &rotation, sizeof(glm::quat));
_reset = false;
@@ -501,5 +510,4 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
} else {
qCWarning(interfaceapp) << "DDE Face Tracker: Decode error";
}
- _lastReceiveTimestamp = usecTimestampNow();
}
diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h
index 9fb0e943f2..9edbd4df58 100644
--- a/interface/src/devices/DdeFaceTracker.h
+++ b/interface/src/devices/DdeFaceTracker.h
@@ -28,6 +28,7 @@ class DdeFaceTracker : public FaceTracker, public Dependency {
SINGLETON_DEPENDENCY
public:
+ virtual void init();
virtual void reset();
virtual bool isActive() const;
@@ -120,9 +121,6 @@ private:
float _lastEyeBlinks[2];
float _filteredEyeBlinks[2];
float _lastEyeCoefficients[2];
-
- bool _isCalculatingFPS;
- int _frameCount;
};
#endif // hifi_DdeFaceTracker_h
diff --git a/interface/src/devices/FaceTracker.cpp b/interface/src/devices/FaceTracker.cpp
index 0d40249c26..25a76ff2b1 100644
--- a/interface/src/devices/FaceTracker.cpp
+++ b/interface/src/devices/FaceTracker.cpp
@@ -15,14 +15,14 @@
#include "FaceTracker.h"
#include "InterfaceLogging.h"
+#include "Menu.h"
const int FPS_TIMER_DELAY = 2000; // ms
const int FPS_TIMER_DURATION = 2000; // ms
-FaceTracker::FaceTracker() :
- _isCalculatingFPS(false),
- _frameCount(0)
-{
+void FaceTracker::init() {
+ _isMuted = Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking);
+ _isInitialized = true; // FaceTracker can be used now
}
inline float FaceTracker::getBlendshapeCoefficient(int index) const {
@@ -101,3 +101,8 @@ void FaceTracker::finishFPSTimer() {
qCDebug(interfaceapp) << "Face tracker FPS =" << (float)_frameCount / ((float)FPS_TIMER_DURATION / 1000.0f);
_isCalculatingFPS = false;
}
+
+void FaceTracker::toggleMute() {
+ _isMuted = !_isMuted;
+ emit muteToggled();
+}
diff --git a/interface/src/devices/FaceTracker.h b/interface/src/devices/FaceTracker.h
index a0a434ee9e..2a0c4438a4 100644
--- a/interface/src/devices/FaceTracker.h
+++ b/interface/src/devices/FaceTracker.h
@@ -26,7 +26,7 @@ public:
virtual bool isActive() const { return false; }
virtual bool isTracking() const { return false; }
- virtual void init() {}
+ virtual void init();
virtual void update(float deltaTime);
virtual void reset();
@@ -42,11 +42,23 @@ public:
bool isValidBlendshapeIndex(int index) const { return index >= 0 && index < getNumBlendshapes(); }
const QVector& getBlendshapeCoefficients() const;
float getBlendshapeCoefficient(int index) const;
-
+
+ bool isMuted() const { return _isMuted; }
+ void setIsMuted(bool isMuted) { _isMuted = isMuted; }
+ void toggleMute();
+
+signals:
+ void muteToggled();
+
+public slots:
+ virtual void setEnabled(bool enabled) = 0;
+
protected:
- FaceTracker();
virtual ~FaceTracker() {};
+ bool _isInitialized = false;
+ bool _isMuted = true;
+
glm::vec3 _headTranslation = glm::vec3(0.0f);
glm::quat _headRotation = glm::quat();
float _estimatedEyePitch = 0.0f;
@@ -63,8 +75,8 @@ private slots:
void finishFPSTimer();
private:
- bool _isCalculatingFPS;
- int _frameCount;
+ bool _isCalculatingFPS = false;
+ int _frameCount = 0;
};
#endif // hifi_FaceTracker_h
diff --git a/interface/src/devices/Faceshift.cpp b/interface/src/devices/Faceshift.cpp
index 183c7695af..409f359afa 100644
--- a/interface/src/devices/Faceshift.cpp
+++ b/interface/src/devices/Faceshift.cpp
@@ -49,7 +49,8 @@ Faceshift::Faceshift() :
#ifdef HAVE_FACESHIFT
void Faceshift::init() {
- setTCPEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift));
+ FaceTracker::init();
+ setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift) && !_isMuted);
}
void Faceshift::update(float deltaTime) {
@@ -92,7 +93,7 @@ void Faceshift::reset() {
bool Faceshift::isActive() const {
const quint64 ACTIVE_TIMEOUT_USECS = 1000000;
- return (usecTimestampNow() - _lastTrackingStateReceived) < ACTIVE_TIMEOUT_USECS;
+ return (usecTimestampNow() - _lastReceiveTimestamp) < ACTIVE_TIMEOUT_USECS;
}
bool Faceshift::isTracking() const {
@@ -127,7 +128,11 @@ void Faceshift::updateFakeCoefficients(float leftBlink, float rightBlink, float
coefficients[FUNNEL_BLENDSHAPE] = mouth3;
}
-void Faceshift::setTCPEnabled(bool enabled) {
+void Faceshift::setEnabled(bool enabled) {
+ // Don't enable until have explicitly initialized
+ if (!_isInitialized) {
+ return;
+ }
#ifdef HAVE_FACESHIFT
if ((_tcpEnabled = enabled)) {
connectSocket();
@@ -196,6 +201,8 @@ void Faceshift::send(const std::string& message) {
void Faceshift::receive(const QByteArray& buffer) {
#ifdef HAVE_FACESHIFT
+ _lastReceiveTimestamp = usecTimestampNow();
+
_stream.received(buffer.size(), buffer.constData());
fsMsgPtr msg;
for (fsMsgPtr msg; (msg = _stream.get_message()); ) {
@@ -240,11 +247,11 @@ void Faceshift::receive(const QByteArray& buffer) {
const float FRAME_AVERAGING_FACTOR = 0.99f;
quint64 usecsNow = usecTimestampNow();
- if (_lastTrackingStateReceived != 0) {
+ if (_lastMessageReceived != 0) {
_averageFrameTime = FRAME_AVERAGING_FACTOR * _averageFrameTime +
- (1.0f - FRAME_AVERAGING_FACTOR) * (float)(usecsNow - _lastTrackingStateReceived) / 1000000.0f;
+ (1.0f - FRAME_AVERAGING_FACTOR) * (float)(usecsNow - _lastMessageReceived) / 1000000.0f;
}
- _lastTrackingStateReceived = usecsNow;
+ _lastMessageReceived = usecsNow;
}
break;
}
diff --git a/interface/src/devices/Faceshift.h b/interface/src/devices/Faceshift.h
index 3d38af5654..9be1766170 100644
--- a/interface/src/devices/Faceshift.h
+++ b/interface/src/devices/Faceshift.h
@@ -87,7 +87,7 @@ signals:
void connectionStateChanged();
public slots:
- void setTCPEnabled(bool enabled);
+ void setEnabled(bool enabled);
private slots:
void connectSocket();
@@ -114,7 +114,8 @@ private:
bool _tcpEnabled = true;
int _tcpRetryCount = 0;
bool _tracking = false;
- quint64 _lastTrackingStateReceived = 0;
+ quint64 _lastReceiveTimestamp = 0;
+ quint64 _lastMessageReceived = 0;
float _averageFrameTime = STARTING_FACESHIFT_FRAME_TIME;
glm::vec3 _headAngularVelocity = glm::vec3(0.0f);
diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp
index 8704a61261..c044bb0674 100644
--- a/interface/src/ui/ApplicationOverlay.cpp
+++ b/interface/src/ui/ApplicationOverlay.cpp
@@ -25,6 +25,7 @@
#include "audio/AudioToolBox.h"
#include "Application.h"
#include "ApplicationOverlay.h"
+#include "devices/CameraToolBox.h"
#include "devices/OculusManager.h"
#include "Util.h"
@@ -211,6 +212,7 @@ void ApplicationOverlay::renderOverlay() {
glMatrixMode(GL_MODELVIEW);
renderAudioMeter();
+ renderCameraToggle();
renderStatsAndLogs();
@@ -808,18 +810,46 @@ void ApplicationOverlay::renderMagnifier(glm::vec2 magPos, float sizeMult, bool
} glPopMatrix();
}
+const int AUDIO_METER_GAP = 5;
+const int MUTE_ICON_PADDING = 10;
+
+void ApplicationOverlay::renderCameraToggle() {
+ if (Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)) {
+ return;
+ }
+
+ int audioMeterY;
+ bool smallMirrorVisible = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !OculusManager::isConnected();
+ bool boxed = smallMirrorVisible &&
+ !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror);
+ if (boxed) {
+ audioMeterY = MIRROR_VIEW_HEIGHT + AUDIO_METER_GAP + MUTE_ICON_PADDING;
+ } else {
+ audioMeterY = AUDIO_METER_GAP + MUTE_ICON_PADDING;
+ }
+
+ DependencyManager::get()->render(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, boxed);
+}
+
void ApplicationOverlay::renderAudioMeter() {
auto glCanvas = Application::getInstance()->getGLWidget();
auto audio = DependencyManager::get();
// Audio VU Meter and Mute Icon
const int MUTE_ICON_SIZE = 24;
- const int MUTE_ICON_PADDING = 10;
- const int AUDIO_METER_WIDTH = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - MUTE_ICON_PADDING;
- const int AUDIO_METER_SCALE_WIDTH = AUDIO_METER_WIDTH - 2 ;
const int AUDIO_METER_HEIGHT = 8;
- const int AUDIO_METER_GAP = 5;
- const int AUDIO_METER_X = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_GAP;
+ const int INTER_ICON_GAP = 2;
+
+ int cameraSpace = 0;
+ int audioMeterWidth = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - MUTE_ICON_PADDING;
+ int audioMeterScaleWidth = audioMeterWidth - 2;
+ int audioMeterX = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_GAP;
+ if (!Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)) {
+ cameraSpace = MUTE_ICON_SIZE + INTER_ICON_GAP;
+ audioMeterWidth -= cameraSpace;
+ audioMeterScaleWidth -= cameraSpace;
+ audioMeterX += cameraSpace;
+ }
int audioMeterY;
bool smallMirrorVisible = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !OculusManager::isConnected();
@@ -834,13 +864,13 @@ void ApplicationOverlay::renderAudioMeter() {
const glm::vec4 AUDIO_METER_BLUE = { 0.0, 0.0, 1.0, 1.0 };
const glm::vec4 AUDIO_METER_GREEN = { 0.0, 1.0, 0.0, 1.0 };
const glm::vec4 AUDIO_METER_RED = { 1.0, 0.0, 0.0, 1.0 };
- const float AUDIO_GREEN_START = 0.25 * AUDIO_METER_SCALE_WIDTH;
- const float AUDIO_RED_START = 0.80 * AUDIO_METER_SCALE_WIDTH;
const float CLIPPING_INDICATOR_TIME = 1.0f;
const float AUDIO_METER_AVERAGING = 0.5;
const float LOG2 = log(2.0f);
const float METER_LOUDNESS_SCALE = 2.8f / 5.0f;
const float LOG2_LOUDNESS_FLOOR = 11.0f;
+ float audioGreenStart = 0.25f * audioMeterScaleWidth;
+ float audioRedStart = 0.8f * audioMeterScaleWidth;
float audioLevel = 0.0f;
float loudness = audio->getLastInputLoudness() + 1.0f;
@@ -848,12 +878,12 @@ void ApplicationOverlay::renderAudioMeter() {
float log2loudness = log(_trailingAudioLoudness) / LOG2;
if (log2loudness <= LOG2_LOUDNESS_FLOOR) {
- audioLevel = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE * AUDIO_METER_SCALE_WIDTH;
+ audioLevel = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE * audioMeterScaleWidth;
} else {
- audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.0f)) * METER_LOUDNESS_SCALE * AUDIO_METER_SCALE_WIDTH;
+ audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.0f)) * METER_LOUDNESS_SCALE * audioMeterScaleWidth;
}
- if (audioLevel > AUDIO_METER_SCALE_WIDTH) {
- audioLevel = AUDIO_METER_SCALE_WIDTH;
+ if (audioLevel > audioMeterScaleWidth) {
+ audioLevel = audioMeterScaleWidth;
}
bool isClipping = ((audio->getTimeSinceLastClip() > 0.0f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME));
@@ -863,7 +893,7 @@ void ApplicationOverlay::renderAudioMeter() {
renderCollisionOverlay(glCanvas->width(), glCanvas->height(), magnitude, 1.0f);
}
- DependencyManager::get()->render(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, boxed);
+ DependencyManager::get()->render(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, cameraSpace, boxed);
DependencyManager::get()->render(glCanvas->width(), glCanvas->height());
DependencyManager::get()->render(WHITE_TEXT, glCanvas->width(), glCanvas->height());
@@ -871,10 +901,10 @@ void ApplicationOverlay::renderAudioMeter() {
audioMeterY += AUDIO_METER_HEIGHT;
// Draw audio meter background Quad
- DependencyManager::get()->renderQuad(AUDIO_METER_X, audioMeterY, AUDIO_METER_WIDTH, AUDIO_METER_HEIGHT,
+ DependencyManager::get()->renderQuad(audioMeterX, audioMeterY, audioMeterWidth, AUDIO_METER_HEIGHT,
glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
- if (audioLevel > AUDIO_RED_START) {
+ if (audioLevel > audioRedStart) {
glm::vec4 quadColor;
if (!isClipping) {
quadColor = AUDIO_METER_RED;
@@ -882,16 +912,16 @@ void ApplicationOverlay::renderAudioMeter() {
quadColor = glm::vec4(1, 1, 1, 1);
}
// Draw Red Quad
- DependencyManager::get()->renderQuad(AUDIO_METER_X + AUDIO_RED_START,
+ DependencyManager::get()->renderQuad(audioMeterX + audioRedStart,
audioMeterY,
- audioLevel - AUDIO_RED_START,
+ audioLevel - audioRedStart,
AUDIO_METER_HEIGHT, quadColor,
_audioRedQuad);
- audioLevel = AUDIO_RED_START;
+ audioLevel = audioRedStart;
}
- if (audioLevel > AUDIO_GREEN_START) {
+ if (audioLevel > audioGreenStart) {
glm::vec4 quadColor;
if (!isClipping) {
quadColor = AUDIO_METER_GREEN;
@@ -899,13 +929,13 @@ void ApplicationOverlay::renderAudioMeter() {
quadColor = glm::vec4(1, 1, 1, 1);
}
// Draw Green Quad
- DependencyManager::get()->renderQuad(AUDIO_METER_X + AUDIO_GREEN_START,
+ DependencyManager::get()->renderQuad(audioMeterX + audioGreenStart,
audioMeterY,
- audioLevel - AUDIO_GREEN_START,
+ audioLevel - audioGreenStart,
AUDIO_METER_HEIGHT, quadColor,
_audioGreenQuad);
- audioLevel = AUDIO_GREEN_START;
+ audioLevel = audioGreenStart;
}
if (audioLevel >= 0) {
@@ -916,7 +946,7 @@ void ApplicationOverlay::renderAudioMeter() {
quadColor = glm::vec4(1, 1, 1, 1);
}
// Draw Blue (low level) quad
- DependencyManager::get()->renderQuad(AUDIO_METER_X,
+ DependencyManager::get()->renderQuad(audioMeterX,
audioMeterY,
audioLevel, AUDIO_METER_HEIGHT, quadColor,
_audioBlueQuad);
diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h
index e6c7526c5d..54c8613f94 100644
--- a/interface/src/ui/ApplicationOverlay.h
+++ b/interface/src/ui/ApplicationOverlay.h
@@ -101,6 +101,7 @@ private:
void renderPointersOculus(const glm::vec3& eyePos);
void renderAudioMeter();
+ void renderCameraToggle();
void renderStatsAndLogs();
void renderDomainConnectionStatusBorder();
diff --git a/interface/src/ui/UserInputMapper.h b/interface/src/ui/UserInputMapper.h
index 32a1782419..ab63bdbef7 100755
--- a/interface/src/ui/UserInputMapper.h
+++ b/interface/src/ui/UserInputMapper.h
@@ -57,7 +57,9 @@ public:
bool isAxis() const { return getType() == ChannelType::AXIS; }
bool isJoint() const { return getType() == ChannelType::JOINT; }
- explicit Input() {}
+ // WORKAROUND: the explicit initializer here avoids a bug in GCC-4.8.2 (but not found in 4.9.2)
+ // where the default initializer (a C++-11ism) for the union data above is not applied.
+ explicit Input() : _id(0) {}
explicit Input(uint32 id) : _id(id) {}
explicit Input(uint16 device, uint16 channel, ChannelType type) : _device(device), _channel(channel), _type(uint16(type)) {}
Input(const Input& src) : _id(src._id) {}
diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp
index 5077595831..24ef1a9cf3 100644
--- a/tests/ui/src/main.cpp
+++ b/tests/ui/src/main.cpp
@@ -162,6 +162,7 @@ public:
Mirror,
MuteAudio,
MuteEnvironment,
+ MuteFaceTracking,
NoFaceTracking,
NoShadows,
OctreeStats,