merge upstream/master into andrew/scripting

Conflicts:
	interface/src/avatar/MyAvatar.cpp
This commit is contained in:
Andrew Meadows 2014-04-10 08:32:14 -07:00
commit 3755aee3e0
26 changed files with 696 additions and 548 deletions

4
.gitignore vendored
View file

@ -42,5 +42,9 @@ interface/external/visage/*
interface/resources/visage/*
!interface/resources/visage/tracker.cfg
# Ignore Faceplus
interface/external/faceplus/*
!interface/external/faceplus/readme.txt
# Ignore interfaceCache for Linux users
interface/interfaceCache/

View file

@ -0,0 +1,42 @@
# Try to find the Faceplus library
#
# You must provide a FACEPLUS_ROOT_DIR which contains lib and include directories
#
# Once done this will define
#
# FACEPLUS_FOUND - system found Faceplus
# FACEPLUS_INCLUDE_DIRS - the Faceplus include directory
# FACEPLUS_LIBRARIES - Link this to use Faceplus
#
# Created on 4/8/2014 by Andrzej Kapolka
# Copyright (c) 2014 High Fidelity
#
if (FACEPLUS_LIBRARIES AND FACEPLUS_INCLUDE_DIRS)
# in cache already
set(FACEPLUS_FOUND TRUE)
else (FACEPLUS_LIBRARIES AND FACEPLUS_INCLUDE_DIRS)
find_path(FACEPLUS_INCLUDE_DIRS faceplus.h ${FACEPLUS_ROOT_DIR}/include)
if (WIN32)
find_library(FACEPLUS_LIBRARIES faceplus.lib ${FACEPLUS_ROOT_DIR}/win32/)
endif (WIN32)
if (FACEPLUS_INCLUDE_DIRS AND FACEPLUS_LIBRARIES)
set(FACEPLUS_FOUND TRUE)
endif (FACEPLUS_INCLUDE_DIRS AND FACEPLUS_LIBRARIES)
if (FACEPLUS_FOUND)
if (NOT FACEPLUS_FIND_QUIETLY)
message(STATUS "Found Faceplus... ${FACEPLUS_LIBRARIES}")
endif (NOT FACEPLUS_FIND_QUIETLY)
else ()
if (FACEPLUS_FIND_REQUIRED)
message(FATAL_ERROR "Could not find Faceplus")
endif (FACEPLUS_FIND_REQUIRED)
endif ()
# show the FACEPLUS_INCLUDE_DIRS and FACEPLUS_LIBRARIES variables only in the advanced view
mark_as_advanced(FACEPLUS_INCLUDE_DIRS FACEPLUS_LIBRARIES)
endif (FACEPLUS_LIBRARIES AND FACEPLUS_INCLUDE_DIRS)

View file

@ -12,6 +12,7 @@ project(${TARGET_NAME})
# setup for find modules
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/")
set(FACEPLUS_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/faceplus")
set(FACESHIFT_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/faceshift")
set(LIBOVR_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/oculus")
set(SIXENSE_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/Sixense")
@ -130,6 +131,7 @@ link_hifi_library(audio ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(script-engine ${TARGET_NAME} "${ROOT_DIR}")
# find any optional libraries
find_package(Faceplus)
find_package(Faceshift)
find_package(LibOVR)
find_package(Sixense)
@ -163,6 +165,13 @@ if (VISAGE_FOUND AND NOT DISABLE_VISAGE)
target_link_libraries(${TARGET_NAME} "${VISAGE_LIBRARIES}")
endif (VISAGE_FOUND AND NOT DISABLE_VISAGE)
# and with Faceplus library, also for webcam feature tracking
if (FACEPLUS_FOUND AND NOT DISABLE_FACEPLUS)
add_definitions(-DHAVE_FACEPLUS)
include_directories(SYSTEM "${FACEPLUS_INCLUDE_DIRS}")
target_link_libraries(${TARGET_NAME} "${FACEPLUS_LIBRARIES}")
endif (FACEPLUS_FOUND AND NOT DISABLE_FACEPLUS)
# and with LibOVR for Oculus Rift
if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR)
add_definitions(-DHAVE_LIBOVR)

11
interface/external/faceplus/readme.txt vendored Normal file
View file

@ -0,0 +1,11 @@
Instructions for adding the Faceplus driver to Interface
Andrzej Kapolka, April 8, 2014
1. Copy the Faceplus sdk folders (include, win32) into the interface/external/faceplus folder.
This readme.txt should be there as well.
2. Copy the Faceplus DLLs from the win32 folder into your path.
3. Delete your build directory, run cmake and build, and you should be all set.

View file

@ -150,7 +150,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_viewFrustum(),
_lastQueriedViewFrustum(),
_lastQueriedTime(usecTimestampNow()),
_audioScope(256, 200, true),
_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
_mouseX(0),
_mouseY(0),
@ -161,7 +160,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_touchAvgY(0.0f),
_isTouchPressed(false),
_mousePressed(false),
_audio(&_audioScope, STARTUP_JITTER_SAMPLES),
_audio(STARTUP_JITTER_SAMPLES),
_enableProcessVoxelsThread(true),
_voxelProcessor(),
_voxelHideShowThread(&_voxels),
@ -342,11 +341,15 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
// clear the scripts, and set out script to our default scripts
clearScriptsBeforeRunning();
loadScript("http://public.highfidelity.io/scripts/defaultScripts.js");
QMutexLocker locker(&_settingsMutex);
_settings->setValue("firstRun",QVariant(false));
} else {
// do this as late as possible so that all required subsystems are inialized
loadScripts();
QMutexLocker locker(&_settingsMutex);
_previousScriptLocation = _settings->value("LastScriptLocation", QVariant("")).toString();
}
}
@ -356,16 +359,12 @@ Application::~Application() {
// make sure we don't call the idle timer any more
delete idleTimer;
Menu::getInstance()->saveSettings();
_rearMirrorTools->saveSettings(_settings);
_sharedVoxelSystem.changeTree(new VoxelTree);
if (_voxelImporter) {
_voxelImporter->saveSettings(_settings);
delete _voxelImporter;
}
_settings->sync();
saveSettings();
delete _voxelImporter;
// let the avatar mixer know we're out
MyAvatar::sendKillAvatar();
@ -399,35 +398,45 @@ Application::~Application() {
AccountManager::getInstance().destroy();
}
void Application::saveSettings() {
Menu::getInstance()->saveSettings();
_rearMirrorTools->saveSettings(_settings);
if (_voxelImporter) {
_voxelImporter->saveSettings(_settings);
}
_settings->sync();
}
void Application::restoreSizeAndPosition() {
QSettings* settings = new QSettings(this);
QRect available = desktop()->availableGeometry();
settings->beginGroup("Window");
QMutexLocker locker(&_settingsMutex);
_settings->beginGroup("Window");
int x = (int)loadSetting(settings, "x", 0);
int y = (int)loadSetting(settings, "y", 0);
int x = (int)loadSetting(_settings, "x", 0);
int y = (int)loadSetting(_settings, "y", 0);
_window->move(x, y);
int width = (int)loadSetting(settings, "width", available.width());
int height = (int)loadSetting(settings, "height", available.height());
int width = (int)loadSetting(_settings, "width", available.width());
int height = (int)loadSetting(_settings, "height", available.height());
_window->resize(width, height);
settings->endGroup();
_settings->endGroup();
}
void Application::storeSizeAndPosition() {
QSettings* settings = new QSettings(this);
QMutexLocker locker(&_settingsMutex);
_settings->beginGroup("Window");
settings->beginGroup("Window");
_settings->setValue("width", _window->rect().width());
_settings->setValue("height", _window->rect().height());
settings->setValue("width", _window->rect().width());
settings->setValue("height", _window->rect().height());
_settings->setValue("x", _window->pos().x());
_settings->setValue("y", _window->pos().y());
settings->setValue("x", _window->pos().x());
settings->setValue("y", _window->pos().y());
settings->endGroup();
_settings->endGroup();
}
void Application::initializeGL() {
@ -744,9 +753,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
case Qt::Key_Period:
Menu::getInstance()->handleViewFrustumOffsetKeyModifier(event->key());
break;
case Qt::Key_Apostrophe:
_audioScope.inputPaused = !_audioScope.inputPaused;
break;
case Qt::Key_L:
if (isShifted) {
Menu::getInstance()->triggerOption(MenuOption::LodTools);
@ -1345,6 +1351,12 @@ glm::vec3 Application::getMouseVoxelWorldCoordinates(const VoxelDetail& mouseVox
(mouseVoxel.z + mouseVoxel.s / 2.f) * TREE_SCALE);
}
FaceTracker* Application::getActiveFaceTracker() {
return _faceshift.isActive() ? static_cast<FaceTracker*>(&_faceshift) :
(_faceplus.isActive() ? static_cast<FaceTracker*>(&_faceplus) :
(_visage.isActive() ? static_cast<FaceTracker*>(&_visage) : NULL));
}
struct SendVoxelsOperationArgs {
const unsigned char* newBaseOctCode;
};
@ -1564,8 +1576,9 @@ void Application::init() {
}
qDebug("Loaded settings");
// initialize Visage and Faceshift after loading the menu settings
// initialize our face trackers after loading the menu settings
_faceshift.init();
_faceplus.init();
_visage.init();
// fire off an immediate domain-server check in now that settings are loaded
@ -1729,19 +1742,11 @@ void Application::updateMyAvatarLookAtPosition() {
glm::distance(_mouseRayOrigin, _myAvatar->getHead()->calculateAverageEyePosition()));
lookAtSpot = _mouseRayOrigin + _mouseRayDirection * qMax(minEyeDistance, distance);
}
bool trackerActive = false;
float eyePitch, eyeYaw;
if (_faceshift.isActive()) {
eyePitch = _faceshift.getEstimatedEyePitch();
eyeYaw = _faceshift.getEstimatedEyeYaw();
trackerActive = true;
} else if (_visage.isActive()) {
eyePitch = _visage.getEstimatedEyePitch();
eyeYaw = _visage.getEstimatedEyeYaw();
trackerActive = true;
}
if (trackerActive) {
FaceTracker* tracker = getActiveFaceTracker();
if (tracker) {
float eyePitch = tracker->getEstimatedEyePitch();
float eyeYaw = tracker->getEstimatedEyeYaw();
// deflect using Faceshift gaze data
glm::vec3 origin = _myAvatar->getHead()->calculateAverageEyePosition();
float pitchSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? -1.0f : 1.0f;
@ -1827,15 +1832,15 @@ void Application::updateCamera(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateCamera()");
if (!OculusManager::isConnected() && !TV3DManager::isConnected()) {
if (Menu::getInstance()->isOptionChecked(MenuOption::OffAxisProjection)) {
float xSign = _myCamera.getMode() == CAMERA_MODE_MIRROR ? 1.0f : -1.0f;
if (_faceshift.isActive()) {
const float EYE_OFFSET_SCALE = 0.025f;
glm::vec3 position = _faceshift.getHeadTranslation() * EYE_OFFSET_SCALE;
_myCamera.setEyeOffsetPosition(glm::vec3(position.x * xSign, position.y, -position.z));
updateProjectionMatrix();
}
if (!OculusManager::isConnected() && !TV3DManager::isConnected() &&
Menu::getInstance()->isOptionChecked(MenuOption::OffAxisProjection)) {
FaceTracker* tracker = getActiveFaceTracker();
if (tracker) {
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;
_myCamera.setEyeOffsetPosition(glm::vec3(position.x * xSign, position.y, -position.z));
updateProjectionMatrix();
}
}
}
@ -2509,15 +2514,6 @@ void Application::displayOverlay() {
}
}
// Audio Scope
const int AUDIO_SCOPE_Y_OFFSET = 135;
if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
if (Menu::getInstance()->isOptionChecked(MenuOption::Oscilloscope)) {
int oscilloscopeTop = _glWidget->height() - AUDIO_SCOPE_Y_OFFSET;
_audioScope.render(MIRROR_VIEW_LEFT_PADDING, oscilloscopeTop);
}
}
// Audio VU Meter and Mute Icon
const int MUTE_ICON_SIZE = 24;
const int AUDIO_METER_INSET = 2;
@ -3181,35 +3177,36 @@ void Application::packetSent(quint64 length) {
void Application::loadScripts() {
// loads all saved scripts
QSettings* settings = new QSettings(this);
int size = settings->beginReadArray("Settings");
int size = lockSettings()->beginReadArray("Settings");
unlockSettings();
for (int i = 0; i < size; ++i){
settings->setArrayIndex(i);
QString string = settings->value("script").toString();
loadScript(string);
lockSettings()->setArrayIndex(i);
QString string = _settings->value("script").toString();
unlockSettings();
if (!string.isEmpty()) {
loadScript(string);
}
}
settings->endArray();
QMutexLocker locker(&_settingsMutex);
_settings->endArray();
}
void Application::clearScriptsBeforeRunning() {
// clears all scripts from the settings
QSettings* settings = new QSettings(this);
settings->beginWriteArray("Settings");
settings->endArray();
QMutexLocker locker(&_settingsMutex);
_settings->remove("Settings");
}
void Application::saveScripts() {
// saves all current running scripts
QSettings* settings = new QSettings(this);
settings->beginWriteArray("Settings");
QMutexLocker locker(&_settingsMutex);
_settings->beginWriteArray("Settings");
for (int i = 0; i < getRunningScripts().size(); ++i){
settings->setArrayIndex(i);
settings->setValue("script", getRunningScripts().at(i));
_settings->setArrayIndex(i);
_settings->setValue("script", getRunningScripts().at(i));
}
settings->endArray();
_settings->endArray();
}
void Application::stopAllScripts() {
@ -3350,7 +3347,10 @@ void Application::loadDialog() {
if (_previousScriptLocation.isEmpty()) {
QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
// Temporary fix to Qt bug: http://stackoverflow.com/questions/16194475
#ifdef __APPLE__
suggestedName = desktopLocation.append("/script.js");
#endif
} else {
suggestedName = _previousScriptLocation;
}
@ -3359,9 +3359,11 @@ void Application::loadDialog() {
tr("JavaScript Files (*.js)"));
if (!fileNameString.isEmpty()) {
_previousScriptLocation = fileNameString;
QMutexLocker locker(&_settingsMutex);
_settings->setValue("LastScriptLocation", _previousScriptLocation);
loadScript(fileNameString);
}
loadScript(fileNameString);
}
void Application::loadScriptURLDialog() {

View file

@ -52,6 +52,7 @@
#include "avatar/Avatar.h"
#include "avatar/AvatarManager.h"
#include "avatar/MyAvatar.h"
#include "devices/Faceplus.h"
#include "devices/Faceshift.h"
#include "devices/SixenseManager.h"
#include "devices/Visage.h"
@ -176,8 +177,10 @@ public:
bool isMouseHidden() const { return _mouseHidden; }
const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
Faceplus* getFaceplus() { return &_faceplus; }
Faceshift* getFaceshift() { return &_faceshift; }
Visage* getVisage() { return &_visage; }
FaceTracker* getActiveFaceTracker();
SixenseManager* getSixenseManager() { return &_sixenseManager; }
BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; }
QUndoStack* getUndoStack() { return &_undoStack; }
@ -185,6 +188,8 @@ public:
/// if you need to access the application settings, use lockSettings()/unlockSettings()
QSettings* lockSettings() { _settingsMutex.lock(); return _settings; }
void unlockSettings() { _settingsMutex.unlock(); }
void saveSettings();
QMainWindow* getWindow() { return _window; }
NodeToOctreeSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; }
@ -316,6 +321,7 @@ private:
// Various helper functions called during update()
void updateLOD();
void updateMouseRay();
void updateFaceplus();
void updateFaceshift();
void updateVisage();
void updateMyAvatarLookAtPosition();
@ -407,7 +413,6 @@ private:
ViewFrustum _shadowViewFrustum;
quint64 _lastQueriedTime;
Oscilloscope _audioScope;
float _trailingAudioLoudness;
OctreeQuery _octreeQuery; // NodeData derived class for querying voxels from voxel server
@ -415,9 +420,10 @@ private:
AvatarManager _avatarManager;
MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be)
Faceplus _faceplus;
Faceshift _faceshift;
Visage _visage;
SixenseManager _sixenseManager;
Camera _myCamera; // My view onto the world

View file

@ -50,7 +50,7 @@ static const int NUMBER_OF_NOISE_SAMPLE_FRAMES = 300;
// Mute icon configration
static const int MUTE_ICON_SIZE = 24;
Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* parent) :
Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) :
AbstractAudioInterface(parent),
_audioInput(NULL),
_desiredInputFormat(),
@ -67,7 +67,6 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* p
_proceduralOutputDevice(NULL),
_inputRingBuffer(0),
_ringBuffer(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL),
_scope(scope),
_averagedLatency(0.0),
_measuredJitter(0),
_jitterBufferSamples(initialJitterBufferSamples),
@ -555,12 +554,6 @@ void Audio::handleAudioInput() {
_lastInputLoudness = 0;
}
}
// add input data just written to the scope
QMetaObject::invokeMethod(_scope, "addSamples", Qt::QueuedConnection,
Q_ARG(QByteArray, QByteArray((char*) monoAudioSamples,
NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL)),
Q_ARG(bool, false), Q_ARG(bool, true));
} else {
// our input loudness is 0, since we're muted
_lastInputLoudness = 0;
@ -724,11 +717,6 @@ void Audio::processReceivedAudio(const QByteArray& audioByteArray) {
if (_outputDevice) {
_outputDevice->write(outputBuffer);
// add output (@speakers) data just written to the scope
QMetaObject::invokeMethod(_scope, "addSamples", Qt::QueuedConnection,
Q_ARG(QByteArray, QByteArray((char*) ringBufferSamples, numNetworkOutputSamples)),
Q_ARG(bool, true), Q_ARG(bool, false));
}
delete[] ringBufferSamples;
}

View file

@ -34,9 +34,6 @@
#include <AudioRingBuffer.h>
#include <StdDev.h>
#include "ui/Oscilloscope.h"
static const int NUM_AUDIO_CHANNELS = 2;
class QAudioInput;
@ -47,7 +44,7 @@ class Audio : public AbstractAudioInterface {
Q_OBJECT
public:
// setup for audio I/O
Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* parent = 0);
Audio(int16_t initialJitterBufferSamples, QObject* parent = 0);
float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _noiseGateMeasuredFloor, 0.f); }
float getTimeSinceLastClip() const { return _timeSinceLastClip; }
@ -126,7 +123,6 @@ private:
QString _inputAudioDeviceName;
QString _outputAudioDeviceName;
Oscilloscope* _scope;
StDev _stdev;
timeval _lastReceiveTime;
float _averagedLatency;

View file

@ -252,7 +252,6 @@ Menu::Menu() :
addDisabledActionAndSeparator(viewMenu, "Stats");
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash);
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L, appInstance, SLOT(toggleLogDialog()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Oscilloscope, 0, false);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Bandwidth, 0, true);
addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails()));
addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0, this, SLOT(octreeStatsDetails()));
@ -304,6 +303,11 @@ Menu::Menu() :
true,
appInstance->getFaceshift(),
SLOT(setTCPEnabled(bool)));
#ifdef HAVE_FACEPLUS
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Faceplus, 0, true,
appInstance->getFaceplus(), SLOT(updateEnabled()));
#endif
#ifdef HAVE_VISAGE
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Visage, 0, true,
appInstance->getVisage(), SLOT(updateEnabled()));

View file

@ -253,6 +253,7 @@ namespace MenuOption {
const QString EchoLocalAudio = "Echo Local Audio";
const QString EchoServerAudio = "Echo Server Audio";
const QString Enable3DTVMode = "Enable 3DTV Mode";
const QString Faceplus = "Faceplus";
const QString Faceshift = "Faceshift";
const QString FilterSixense = "Smooth Sixense Movement";
const QString FirstPerson = "First Person";
@ -287,7 +288,6 @@ namespace MenuOption {
const QString OctreeStats = "Voxel and Particle Statistics";
const QString OffAxisProjection = "Off-Axis Projection";
const QString OldVoxelCullingMode = "Old Voxel Culling Mode";
const QString Oscilloscope = "Audio Oscilloscope";
const QString Pair = "Pair";
const QString Particles = "Particles";
const QString PasteToVoxel = "Paste to Voxel...";

View file

@ -22,7 +22,9 @@
#include <AccountManager.h>
#include "Application.h"
#include "renderer/FBXReader.h"
#include "ModelUploader.h"
@ -34,6 +36,8 @@ static const QString LOD_FIELD = "lod";
static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com";
static const QString MODEL_URL = "/api/v1/models";
static const QString SETTING_NAME = "LastModelUploadLocation";
static const int MAX_SIZE = 10 * 1024 * 1024; // 10 MB
static const int TIMEOUT = 1000;
static const int MAX_CHECK = 30;
@ -59,14 +63,27 @@ ModelUploader::~ModelUploader() {
bool ModelUploader::zip() {
// File Dialog
QString filename = QFileDialog::getOpenFileName(NULL,
"Select your .fst file ...",
QStandardPaths::writableLocation(QStandardPaths::HomeLocation),
"*.fst");
QSettings* settings = Application::getInstance()->lockSettings();
QString lastLocation = settings->value(SETTING_NAME).toString();
if (lastLocation.isEmpty()) {
lastLocation = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
// Temporary fix to Qt bug: http://stackoverflow.com/questions/16194475
#ifdef __APPLE__
lastLocation.append("/model.fst");
#endif
}
QString filename = QFileDialog::getOpenFileName(NULL, "Select your .fst file ...", lastLocation, "*.fst");
if (filename == "") {
// If the user canceled we return.
Application::getInstance()->unlockSettings();
return false;
}
settings->setValue(SETTING_NAME, filename);
Application::getInstance()->unlockSettings();
bool _nameIsPresent = false;
QString texDir;
QString fbxFile;

View file

@ -64,17 +64,11 @@ void Head::reset() {
void Head::simulate(float deltaTime, bool isMine, bool billboard) {
// Update audio trailing average for rendering facial animations
Faceshift* faceshift = Application::getInstance()->getFaceshift();
Visage* visage = Application::getInstance()->getVisage();
if (isMine) {
_isFaceshiftConnected = false;
if (faceshift->isActive()) {
_blendshapeCoefficients = faceshift->getBlendshapeCoefficients();
_isFaceshiftConnected = true;
} else if (visage->isActive()) {
_blendshapeCoefficients = visage->getBlendshapeCoefficients();
_isFaceshiftConnected = true;
FaceTracker* faceTracker = Application::getInstance()->getActiveFaceTracker();
if ((_isFaceshiftConnected = faceTracker)) {
_blendshapeCoefficients = faceTracker->getBlendshapeCoefficients();
_isFaceshiftConnected = true;
}
}
@ -156,8 +150,9 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
const float BROW_LIFT_SCALE = 500.0f;
const float JAW_OPEN_SCALE = 0.01f;
const float JAW_OPEN_DEAD_ZONE = 0.75f;
faceshift->updateFakeCoefficients(_leftEyeBlink, _rightEyeBlink, min(1.0f, _browAudioLift * BROW_LIFT_SCALE),
glm::clamp(sqrt(_averageLoudness * JAW_OPEN_SCALE) - JAW_OPEN_DEAD_ZONE, 0.0f, 1.0f), _blendshapeCoefficients);
Application::getInstance()->getFaceshift()->updateFakeCoefficients(_leftEyeBlink, _rightEyeBlink,
min(1.0f, _browAudioLift * BROW_LIFT_SCALE), glm::clamp(sqrt(_averageLoudness * JAW_OPEN_SCALE) -
JAW_OPEN_DEAD_ZONE, 0.0f, 1.0f), _blendshapeCoefficients);
}
if (!isMine) {

View file

@ -65,7 +65,8 @@ MyAvatar::MyAvatar() :
_moveTargetStepCounter(0),
_lookAtTargetAvatar(),
_shouldRender(true),
_billboardValid(false)
_billboardValid(false),
_oculusYawOffset(0.0f)
{
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
_driveKeys[i] = 0.0f;
@ -88,10 +89,11 @@ void MyAvatar::reset() {
_skeletonModel.reset();
getHead()->reset();
getHand()->reset();
_oculusYawOffset = 0.0f;
setVelocity(glm::vec3(0.f));
setThrust(glm::vec3(0.f));
setOrientation(glm::quat(glm::vec3(0.f)));
setVelocity(glm::vec3(0.0f));
setThrust(glm::vec3(0.0f));
setOrientation(glm::quat(glm::vec3(0.0f)));
}
void MyAvatar::setMoveTarget(const glm::vec3 moveTarget) {
@ -114,8 +116,8 @@ void MyAvatar::update(float deltaTime) {
// TODO? resurrect headMouse stuff?
//glm::vec3 headVelocity = faceshift->getHeadAngularVelocity();
//// sets how quickly head angular rotation moves the head mouse
//const float HEADMOUSE_FACESHIFT_YAW_SCALE = 40.f;
//const float HEADMOUSE_FACESHIFT_PITCH_SCALE = 30.f;
//const float HEADMOUSE_FACESHIFT_YAW_SCALE = 40.0f;
//const float HEADMOUSE_FACESHIFT_PITCH_SCALE = 30.0f;
//_headMouseX -= headVelocity.y * HEADMOUSE_FACESHIFT_YAW_SCALE;
//_headMouseY -= headVelocity.x * HEADMOUSE_FACESHIFT_PITCH_SCALE;
//
@ -124,16 +126,6 @@ void MyAvatar::update(float deltaTime) {
//_headMouseY = glm::clamp(_headMouseY, 0, _glWidget->height());
}
if (OculusManager::isConnected()) {
float yaw, pitch, roll; // these angles will be in radians
OculusManager::getEulerAngles(yaw, pitch, roll);
// but these euler angles are stored in degrees
head->setBaseYaw(yaw * DEGREES_PER_RADIAN);
head->setBasePitch(pitch * DEGREES_PER_RADIAN);
head->setBaseRoll(roll * DEGREES_PER_RADIAN);
}
// Get audio loudness data from audio input device
Audio* audio = Application::getInstance()->getAudio();
head->setAudioLoudness(audio->getLastInputLoudness());
@ -156,15 +148,15 @@ void MyAvatar::simulate(float deltaTime) {
_elapsedTimeSinceCollision += deltaTime;
const float VELOCITY_MOVEMENT_TIMER_THRESHOLD = 0.2f;
if (glm::length(_velocity) < VELOCITY_MOVEMENT_TIMER_THRESHOLD) {
_elapsedTimeMoving = 0.f;
_elapsedTimeMoving = 0.0f;
_elapsedTimeStopped += deltaTime;
} else {
_elapsedTimeStopped = 0.f;
_elapsedTimeStopped = 0.0f;
_elapsedTimeMoving += deltaTime;
}
if (_scale != _targetScale) {
float scale = (1.f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale;
float scale = (1.0f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale;
setScale(scale);
Application::getInstance()->getCamera()->setScale(scale);
}
@ -192,19 +184,19 @@ void MyAvatar::simulate(float deltaTime) {
// decay body rotation momentum
const float BODY_SPIN_FRICTION = 7.5f;
float bodySpinMomentum = 1.f - BODY_SPIN_FRICTION * deltaTime;
float bodySpinMomentum = 1.0f - BODY_SPIN_FRICTION * deltaTime;
if (bodySpinMomentum < 0.0f) { bodySpinMomentum = 0.0f; }
_bodyPitchDelta *= bodySpinMomentum;
_bodyYawDelta *= bodySpinMomentum;
_bodyRollDelta *= bodySpinMomentum;
float MINIMUM_ROTATION_RATE = 2.0f;
if (fabs(_bodyYawDelta) < MINIMUM_ROTATION_RATE) { _bodyYawDelta = 0.f; }
if (fabs(_bodyRollDelta) < MINIMUM_ROTATION_RATE) { _bodyRollDelta = 0.f; }
if (fabs(_bodyPitchDelta) < MINIMUM_ROTATION_RATE) { _bodyPitchDelta = 0.f; }
if (fabs(_bodyYawDelta) < MINIMUM_ROTATION_RATE) { _bodyYawDelta = 0.0f; }
if (fabs(_bodyRollDelta) < MINIMUM_ROTATION_RATE) { _bodyRollDelta = 0.0f; }
if (fabs(_bodyPitchDelta) < MINIMUM_ROTATION_RATE) { _bodyPitchDelta = 0.0f; }
const float MAX_STATIC_FRICTION_SPEED = 0.5f;
const float STATIC_FRICTION_STRENGTH = _scale * 20.f;
const float STATIC_FRICTION_STRENGTH = _scale * 20.0f;
applyStaticFriction(deltaTime, _velocity, MAX_STATIC_FRICTION_SPEED, STATIC_FRICTION_STRENGTH);
// Damp avatar velocity
@ -212,11 +204,11 @@ void MyAvatar::simulate(float deltaTime) {
const float SPEED_BRAKE_POWER = _scale * 10.0f;
const float SQUARED_DAMPING_STRENGTH = 0.007f;
const float SLOW_NEAR_RADIUS = 5.f;
const float SLOW_NEAR_RADIUS = 5.0f;
float linearDamping = LINEAR_DAMPING_STRENGTH;
const float NEAR_AVATAR_DAMPING_FACTOR = 50.f;
const float NEAR_AVATAR_DAMPING_FACTOR = 50.0f;
if (_distanceToNearestAvatar < _scale * SLOW_NEAR_RADIUS) {
linearDamping *= 1.f + NEAR_AVATAR_DAMPING_FACTOR *
linearDamping *= 1.0f + NEAR_AVATAR_DAMPING_FACTOR *
((SLOW_NEAR_RADIUS - _distanceToNearestAvatar) / SLOW_NEAR_RADIUS);
}
if (_speedBrakes) {
@ -225,6 +217,54 @@ void MyAvatar::simulate(float deltaTime) {
applyDamping(deltaTime, _velocity, linearDamping, SQUARED_DAMPING_STRENGTH);
}
if (OculusManager::isConnected()) {
// these angles will be in radians
float yaw, pitch, roll;
OculusManager::getEulerAngles(yaw, pitch, roll);
// ... so they need to be converted to degrees before we do math...
// The neck is limited in how much it can yaw, so we check its relative
// yaw from the body and yaw the body if necessary.
yaw *= DEGREES_PER_RADIAN;
float bodyToHeadYaw = yaw - _oculusYawOffset;
const float MAX_NECK_YAW = 85.0f; // degrees
if ((fabs(bodyToHeadYaw) > 2.0f * MAX_NECK_YAW) && (yaw * _oculusYawOffset < 0.0f)) {
// We've wrapped around the range for yaw so adjust
// the measured yaw to be relative to _oculusYawOffset.
if (yaw > 0.0f) {
yaw -= 360.0f;
} else {
yaw += 360.0f;
}
bodyToHeadYaw = yaw - _oculusYawOffset;
}
float delta = fabs(bodyToHeadYaw) - MAX_NECK_YAW;
if (delta > 0.0f) {
yaw = MAX_NECK_YAW;
if (bodyToHeadYaw < 0.0f) {
delta *= -1.0f;
bodyToHeadYaw = -MAX_NECK_YAW;
} else {
bodyToHeadYaw = MAX_NECK_YAW;
}
// constrain _oculusYawOffset to be within range [-180,180]
_oculusYawOffset = fmod((_oculusYawOffset + delta) + 180.0f, 360.0f) - 180.0f;
// We must adjust the body orientation using a delta rotation (rather than
// doing yaw math) because the body's yaw ranges are not the same
// as what the Oculus API provides.
glm::vec3 UP_AXIS = glm::vec3(0.0f, 1.0f, 0.0f);
glm::quat bodyCorrection = glm::angleAxis(glm::radians(delta), UP_AXIS);
orientation = orientation * bodyCorrection;
}
Head* head = getHead();
head->setBaseYaw(bodyToHeadYaw);
head->setBasePitch(pitch * DEGREES_PER_RADIAN);
head->setBaseRoll(roll * DEGREES_PER_RADIAN);
}
// update the euler angles
setOrientation(orientation);
@ -243,7 +283,7 @@ void MyAvatar::simulate(float deltaTime) {
// If a move target is set, update position explicitly
const float MOVE_FINISHED_TOLERANCE = 0.1f;
const float MOVE_SPEED_FACTOR = 2.f;
const float MOVE_SPEED_FACTOR = 2.0f;
const int MOVE_TARGET_MAX_STEPS = 250;
if ((glm::length(_moveTarget) > EPSILON) && (_moveTargetStepCounter < MOVE_TARGET_MAX_STEPS)) {
if (glm::length(_position - _moveTarget) > MOVE_FINISHED_TOLERANCE) {
@ -283,7 +323,7 @@ void MyAvatar::simulate(float deltaTime) {
head->simulate(deltaTime, true);
// Zero thrust out now that we've added it to velocity in this frame
_thrust = glm::vec3(0.f);
_thrust = glm::vec3(0.0f);
// now that we're done stepping the avatar forward in time, compute new collisions
if (_collisionFlags != 0) {
@ -291,7 +331,7 @@ void MyAvatar::simulate(float deltaTime) {
float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE;
if (myCamera->getMode() == CAMERA_MODE_FIRST_PERSON && !OculusManager::isConnected()) {
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.f));
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.0f));
radius *= COLLISION_RADIUS_SCALAR;
}
@ -312,45 +352,35 @@ void MyAvatar::simulate(float deltaTime) {
// Update avatar head rotation with sensor data
void MyAvatar::updateFromGyros(float deltaTime) {
Faceshift* faceshift = Application::getInstance()->getFaceshift();
Visage* visage = Application::getInstance()->getVisage();
glm::vec3 estimatedPosition, estimatedRotation;
bool trackerActive = false;
if (faceshift->isActive()) {
estimatedPosition = faceshift->getHeadTranslation();
estimatedRotation = glm::degrees(safeEulerAngles(faceshift->getHeadRotation()));
trackerActive = true;
} else if (visage->isActive()) {
estimatedPosition = visage->getHeadTranslation();
estimatedRotation = glm::degrees(safeEulerAngles(visage->getHeadRotation()));
trackerActive = true;
}
Head* head = getHead();
if (trackerActive) {
FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker();
if (tracker) {
estimatedPosition = tracker->getHeadTranslation();
estimatedRotation = glm::degrees(safeEulerAngles(tracker->getHeadRotation()));
// Rotate the body if the head is turned beyond the screen
if (Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)) {
const float TRACKER_YAW_TURN_SENSITIVITY = 0.5f;
const float TRACKER_MIN_YAW_TURN = 15.f;
const float TRACKER_MAX_YAW_TURN = 50.f;
const float TRACKER_MIN_YAW_TURN = 15.0f;
const float TRACKER_MAX_YAW_TURN = 50.0f;
if ( (fabs(estimatedRotation.y) > TRACKER_MIN_YAW_TURN) &&
(fabs(estimatedRotation.y) < TRACKER_MAX_YAW_TURN) ) {
if (estimatedRotation.y > 0.f) {
if (estimatedRotation.y > 0.0f) {
_bodyYawDelta += (estimatedRotation.y - TRACKER_MIN_YAW_TURN) * TRACKER_YAW_TURN_SENSITIVITY;
} else {
_bodyYawDelta += (estimatedRotation.y + TRACKER_MIN_YAW_TURN) * TRACKER_YAW_TURN_SENSITIVITY;
}
}
}
}
}
// Set the rotation of the avatar's head (as seen by others, not affecting view frustum)
// to be scaled. Pitch is greater to emphasize nodding behavior / synchrony.
const float AVATAR_HEAD_PITCH_MAGNIFY = 1.0f;
const float AVATAR_HEAD_YAW_MAGNIFY = 1.0f;
const float AVATAR_HEAD_ROLL_MAGNIFY = 1.0f;
Head* head = getHead();
head->setDeltaPitch(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY);
head->setDeltaYaw(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY);
head->setDeltaRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY);
@ -375,20 +405,20 @@ void MyAvatar::moveWithLean() {
float leanSideways = head->getLeanSideways();
// Degrees of 'dead zone' when leaning, and amount of acceleration to apply to lean angle
const float LEAN_FWD_DEAD_ZONE = 15.f;
const float LEAN_SIDEWAYS_DEAD_ZONE = 10.f;
const float LEAN_FWD_THRUST_SCALE = 4.f;
const float LEAN_SIDEWAYS_THRUST_SCALE = 3.f;
const float LEAN_FWD_DEAD_ZONE = 15.0f;
const float LEAN_SIDEWAYS_DEAD_ZONE = 10.0f;
const float LEAN_FWD_THRUST_SCALE = 4.0f;
const float LEAN_SIDEWAYS_THRUST_SCALE = 3.0f;
if (fabs(leanForward) > LEAN_FWD_DEAD_ZONE) {
if (leanForward > 0.f) {
if (leanForward > 0.0f) {
addThrust(front * -(leanForward - LEAN_FWD_DEAD_ZONE) * LEAN_FWD_THRUST_SCALE);
} else {
addThrust(front * -(leanForward + LEAN_FWD_DEAD_ZONE) * LEAN_FWD_THRUST_SCALE);
}
}
if (fabs(leanSideways) > LEAN_SIDEWAYS_DEAD_ZONE) {
if (leanSideways > 0.f) {
if (leanSideways > 0.0f) {
addThrust(right * -(leanSideways - LEAN_SIDEWAYS_DEAD_ZONE) * LEAN_SIDEWAYS_THRUST_SCALE);
} else {
addThrust(right * -(leanSideways + LEAN_SIDEWAYS_DEAD_ZONE) * LEAN_SIDEWAYS_THRUST_SCALE);
@ -435,7 +465,7 @@ void MyAvatar::renderHeadMouse() const {
// TODO? resurrect headMouse stuff?
/*
// Display small target box at center or head mouse target that can also be used to measure LOD
glColor3f(1.f, 1.f, 1.f);
glColor3f(1.0f, 1.0f, 1.0f);
glDisable(GL_LINE_SMOOTH);
const int PIXEL_BOX = 16;
glBegin(GL_LINES);
@ -445,7 +475,7 @@ void MyAvatar::renderHeadMouse() const {
glVertex2f(_headMouseX, _headMouseY + PIXEL_BOX/2);
glEnd();
glEnable(GL_LINE_SMOOTH);
glColor3f(1.f, 0.f, 0.f);
glColor3f(1.0f, 0.0f, 0.0f);
glPointSize(3.0f);
glDisable(GL_POINT_SMOOTH);
glBegin(GL_POINTS);
@ -457,7 +487,7 @@ void MyAvatar::renderHeadMouse() const {
int eyeTargetX = (_glWidget->width() / 2) - _faceshift.getEstimatedEyeYaw() * EYE_TARGET_PIXELS_PER_DEGREE;
int eyeTargetY = (_glWidget->height() / 2) - _faceshift.getEstimatedEyePitch() * EYE_TARGET_PIXELS_PER_DEGREE;
glColor3f(0.f, 1.f, 1.f);
glColor3f(0.0f, 1.0f, 1.0f);
glDisable(GL_LINE_SMOOTH);
glBegin(GL_LINES);
glVertex2f(eyeTargetX - PIXEL_BOX/2, eyeTargetY);
@ -578,11 +608,6 @@ void MyAvatar::clearLookAtTargetAvatar() {
_lookAtTargetAvatar.clear();
}
float MyAvatar::getAbsoluteHeadYaw() const {
const Head* head = static_cast<const Head*>(_headData);
return glm::yaw(head->getOrientation());
}
glm::vec3 MyAvatar::getUprightHeadPosition() const {
return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, getPelvisToHeadLength(), 0.0f);
}
@ -641,11 +666,11 @@ void MyAvatar::updateThrust(float deltaTime) {
glm::vec3 up = orientation * IDENTITY_UP;
const float THRUST_MAG_UP = 800.0f;
const float THRUST_MAG_DOWN = 300.f;
const float THRUST_MAG_FWD = 500.f;
const float THRUST_MAG_BACK = 300.f;
const float THRUST_MAG_LATERAL = 250.f;
const float THRUST_JUMP = 120.f;
const float THRUST_MAG_DOWN = 300.0f;
const float THRUST_MAG_FWD = 500.0f;
const float THRUST_MAG_BACK = 300.0f;
const float THRUST_MAG_LATERAL = 250.0f;
const float THRUST_JUMP = 120.0f;
// Add Thrusts from keyboard
_thrust += _driveKeys[FWD] * _scale * THRUST_MAG_FWD * _thrustMultiplier * deltaTime * front;
@ -656,25 +681,25 @@ void MyAvatar::updateThrust(float deltaTime) {
_thrust -= _driveKeys[DOWN] * _scale * THRUST_MAG_DOWN * _thrustMultiplier * deltaTime * up;
// attenuate thrust when in penetration
if (glm::dot(_thrust, _lastBodyPenetration) > 0.f) {
if (glm::dot(_thrust, _lastBodyPenetration) > 0.0f) {
const float MAX_BODY_PENETRATION_DEPTH = 0.6f * _skeletonModel.getBoundingShapeRadius();
float penetrationFactor = glm::min(1.f, glm::length(_lastBodyPenetration) / MAX_BODY_PENETRATION_DEPTH);
float penetrationFactor = glm::min(1.0f, glm::length(_lastBodyPenetration) / MAX_BODY_PENETRATION_DEPTH);
glm::vec3 penetrationDirection = glm::normalize(_lastBodyPenetration);
// attenuate parallel component
glm::vec3 parallelThrust = glm::dot(_thrust, penetrationDirection) * penetrationDirection;
// attenuate perpendicular component (friction)
glm::vec3 perpendicularThrust = _thrust - parallelThrust;
// recombine to get the final thrust
_thrust = (1.f - penetrationFactor) * parallelThrust + (1.f - penetrationFactor * penetrationFactor) * perpendicularThrust;
_thrust = (1.0f - penetrationFactor) * parallelThrust + (1.0f - penetrationFactor * penetrationFactor) * perpendicularThrust;
// attenuate the growth of _thrustMultiplier when in penetration
// otherwise the avatar will eventually be able to tunnel through the obstacle
_thrustMultiplier *= (1.f - penetrationFactor * penetrationFactor);
} else if (_thrustMultiplier < 1.f) {
_thrustMultiplier *= (1.0f - penetrationFactor * penetrationFactor);
} else if (_thrustMultiplier < 1.0f) {
// rapid healing of attenuated thrustMultiplier after penetration event
_thrustMultiplier = 1.f;
_thrustMultiplier = 1.0f;
}
_lastBodyPenetration = glm::vec3(0.f);
_lastBodyPenetration = glm::vec3(0.0f);
_bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime;
_bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_SPEED * deltaTime;
@ -684,12 +709,12 @@ void MyAvatar::updateThrust(float deltaTime) {
if (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) {
const float THRUST_INCREASE_RATE = 1.05f;
const float MAX_THRUST_MULTIPLIER = 75.0f;
_thrustMultiplier *= 1.f + deltaTime * THRUST_INCREASE_RATE;
_thrustMultiplier *= 1.0f + deltaTime * THRUST_INCREASE_RATE;
if (_thrustMultiplier > MAX_THRUST_MULTIPLIER) {
_thrustMultiplier = MAX_THRUST_MULTIPLIER;
}
} else {
_thrustMultiplier = 1.f;
_thrustMultiplier = 1.0f;
}
// Add one time jumping force if requested
@ -794,11 +819,11 @@ void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity
if (penetrationLength > EPSILON) {
_elapsedTimeSinceCollision = 0.0f;
glm::vec3 direction = penetration / penetrationLength;
_velocity -= glm::dot(_velocity, direction) * direction * (1.f + elasticity);
_velocity *= glm::clamp(1.f - damping, 0.0f, 1.0f);
if ((glm::length(_velocity) < HALTING_VELOCITY) && (glm::length(_thrust) == 0.f)) {
_velocity -= glm::dot(_velocity, direction) * direction * (1.0f + elasticity);
_velocity *= glm::clamp(1.0f - damping, 0.0f, 1.0f);
if ((glm::length(_velocity) < HALTING_VELOCITY) && (glm::length(_thrust) == 0.0f)) {
// If moving really slowly after a collision, and not applying forces, stop altogether
_velocity *= 0.f;
_velocity *= 0.0f;
}
}
}
@ -806,7 +831,7 @@ void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity
void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTime, float frequency) {
// consider whether to have the collision make a sound
const float AUDIBLE_COLLISION_THRESHOLD = 0.02f;
const float COLLISION_LOUDNESS = 1.f;
const float COLLISION_LOUDNESS = 1.0f;
const float DURATION_SCALING = 0.004f;
const float NOISE_SCALING = 0.1f;
glm::vec3 velocity = _velocity;
@ -826,10 +851,10 @@ void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTim
// Noise is a function of the angle of collision
// Duration of the sound is a function of both base frequency and velocity of impact
Application::getInstance()->getAudio()->startCollisionSound(
std::min(COLLISION_LOUDNESS * velocityTowardCollision, 1.f),
frequency * (1.f + velocityTangentToCollision / velocityTowardCollision),
std::min(velocityTangentToCollision / velocityTowardCollision * NOISE_SCALING, 1.f),
1.f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, true);
std::min(COLLISION_LOUDNESS * velocityTowardCollision, 1.0f),
frequency * (1.0f + velocityTangentToCollision / velocityTowardCollision),
std::min(velocityTangentToCollision / velocityTowardCollision * NOISE_SCALING, 1.0f),
1.0f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, true);
}
}
@ -842,8 +867,8 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float
float halfHeights = 0.5 * (heightA + heightB);
if (yDistance < halfHeights) {
// cylinders collide
if (xzDistance > 0.f) {
positionBA.y = 0.f;
if (xzDistance > 0.0f) {
positionBA.y = 0.0f;
// note, penetration should point from A into B
penetration = positionBA * ((radiusA + radiusB - xzDistance) / xzDistance);
return true;
@ -853,7 +878,7 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float
}
} else if (yDistance < halfHeights + radiusA + radiusB) {
// caps collide
if (positionBA.y < 0.f) {
if (positionBA.y < 0.0f) {
// A is above B
positionBA.y += halfHeights;
float BA = glm::length(positionBA);
@ -1068,15 +1093,15 @@ void MyAvatar::goHome() {
}
void MyAvatar::increaseSize() {
if ((1.f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) {
_targetScale *= (1.f + SCALING_RATIO);
if ((1.0f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) {
_targetScale *= (1.0f + SCALING_RATIO);
qDebug("Changed scale to %f", _targetScale);
}
}
void MyAvatar::decreaseSize() {
if (MIN_AVATAR_SCALE < (1.f - SCALING_RATIO) * _targetScale) {
_targetScale *= (1.f - SCALING_RATIO);
if (MIN_AVATAR_SCALE < (1.0f - SCALING_RATIO) * _targetScale) {
_targetScale *= (1.0f - SCALING_RATIO);
qDebug("Changed scale to %f", _targetScale);
}
}
@ -1152,8 +1177,8 @@ void MyAvatar::applyCollision(const glm::vec3& contactPoint, const glm::vec3& pe
if (leverLength > EPSILON) {
// compute lean perturbation angles
glm::quat bodyRotation = getOrientation();
glm::vec3 xAxis = bodyRotation * glm::vec3(1.f, 0.f, 0.f);
glm::vec3 zAxis = bodyRotation * glm::vec3(0.f, 0.f, 1.f);
glm::vec3 xAxis = bodyRotation * glm::vec3(1.0f, 0.0f, 0.0f);
glm::vec3 zAxis = bodyRotation * glm::vec3(0.0f, 0.0f, 1.0f);
leverAxis = leverAxis / leverLength;
glm::vec3 effectivePenetration = penetration - glm::dot(penetration, leverAxis) * leverAxis;

View file

@ -57,7 +57,6 @@ public:
float getLeanScale() const { return _leanScale; }
float getElapsedTimeStopped() const { return _elapsedTimeStopped; }
float getElapsedTimeMoving() const { return _elapsedTimeMoving; }
float getAbsoluteHeadYaw() const; // degrees
const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
glm::vec3 getGravity() const { return _gravity; }
@ -128,8 +127,8 @@ private:
QWeakPointer<AvatarData> _lookAtTargetAvatar;
glm::vec3 _targetAvatarPosition;
bool _shouldRender;
bool _billboardValid;
float _oculusYawOffset;
// private methods
void updateThrust(float deltaTime);

View file

@ -0,0 +1,17 @@
//
// FaceTracker.cpp
// interface/src/devices
//
// Created by Andrzej Kapolka on 4/9/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 "FaceTracker.h"
FaceTracker::FaceTracker() :
_estimatedEyePitch(0.0f),
_estimatedEyeYaw(0.0f) {
}

View file

@ -0,0 +1,46 @@
//
// FaceTracker.h
// interface/src/devices
//
// Created by Andrzej Kapolka on 4/9/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_FaceTracker_h
#define hifi_FaceTracker_h
#include <QObject>
#include <QVector>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
/// Base class for face trackers (Faceshift, Visage, Faceplus).
class FaceTracker : public QObject {
Q_OBJECT
public:
FaceTracker();
const glm::vec3& getHeadTranslation() const { return _headTranslation; }
const glm::quat& getHeadRotation() const { return _headRotation; }
float getEstimatedEyePitch() const { return _estimatedEyePitch; }
float getEstimatedEyeYaw() const { return _estimatedEyeYaw; }
const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
protected:
glm::vec3 _headTranslation;
glm::quat _headRotation;
float _estimatedEyePitch;
float _estimatedEyeYaw;
QVector<float> _blendshapeCoefficients;
};
#endif // hifi_FaceTracker_h

View file

@ -0,0 +1,230 @@
//
// Faceplus.cpp
// interface/src/devices
//
// Created by Andrzej Kapolka on 4/9/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 <QThread>
#ifdef HAVE_FACEPLUS
#include <faceplus.h>
#endif
#include "Application.h"
#include "Faceplus.h"
#include "renderer/FBXReader.h"
static int floatVectorMetaTypeId = qRegisterMetaType<QVector<float> >();
Faceplus::Faceplus() :
_enabled(false),
_active(false) {
#ifdef HAVE_FACEPLUS
// these are ignored--any values will do
faceplus_log_in("username", "password");
#endif
}
Faceplus::~Faceplus() {
setEnabled(false);
}
void Faceplus::init() {
connect(Application::getInstance()->getFaceshift(), SIGNAL(connectionStateChanged()), SLOT(updateEnabled()));
updateEnabled();
}
void Faceplus::setState(const glm::quat& headRotation, float estimatedEyePitch, float estimatedEyeYaw,
const QVector<float>& blendshapeCoefficients) {
_headRotation = headRotation;
_estimatedEyePitch = estimatedEyePitch;
_estimatedEyeYaw = estimatedEyeYaw;
_blendshapeCoefficients = blendshapeCoefficients;
_active = true;
}
void Faceplus::updateEnabled() {
setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceplus) &&
!(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift) &&
Application::getInstance()->getFaceshift()->isConnectedOrConnecting()));
}
void Faceplus::setEnabled(bool enabled) {
if (_enabled == enabled) {
return;
}
if ((_enabled = enabled)) {
_reader = new FaceplusReader();
QThread* readerThread = new QThread(this);
_reader->moveToThread(readerThread);
readerThread->start();
QMetaObject::invokeMethod(_reader, "init");
} else {
QThread* readerThread = _reader->thread();
QMetaObject::invokeMethod(_reader, "shutdown");
readerThread->wait();
delete readerThread;
_active = false;
}
}
#ifdef HAVE_FACEPLUS
static QMultiHash<QByteArray, QPair<int, float> > createChannelNameMap() {
QMultiHash<QByteArray, QPair<QByteArray, float> > blendshapeMap;
blendshapeMap.insert("EyeBlink_L", QPair<QByteArray, float>("Mix::Blink_Left", 1.0f));
blendshapeMap.insert("EyeBlink_R", QPair<QByteArray, float>("Mix::Blink_Right", 1.0f));
blendshapeMap.insert("BrowsD_L", QPair<QByteArray, float>("Mix::BrowsDown_Left", 1.0f));
blendshapeMap.insert("BrowsD_R", QPair<QByteArray, float>("Mix::BrowsDown_Right", 1.0f));
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::BrowsIn_Left", 1.0f));
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::BrowsIn_Right", 1.0f));
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::BrowsOuterLower_Left", 1.0f));
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::BrowsOuterLower_Right", 1.0f));
blendshapeMap.insert("BrowsU_L", QPair<QByteArray, float>("Mix::BrowsUp_Left", 10.0f));
blendshapeMap.insert("BrowsU_R", QPair<QByteArray, float>("Mix::BrowsUp_Right", 10.0f));
blendshapeMap.insert("EyeOpen_L", QPair<QByteArray, float>("Mix::EyesWide_Left", 1.0f));
blendshapeMap.insert("EyeOpen_R", QPair<QByteArray, float>("Mix::EyesWide_Right", 1.0f));
blendshapeMap.insert("MouthFrown_L", QPair<QByteArray, float>("Mix::Frown_Left", 1.0f));
blendshapeMap.insert("MouthFrown_R", QPair<QByteArray, float>("Mix::Frown_Right", 1.0f));
blendshapeMap.insert("JawLeft", QPair<QByteArray, float>("Mix::Jaw_RotateY_Left", 1.0f));
blendshapeMap.insert("JawRight", QPair<QByteArray, float>("Mix::Jaw_RotateY_Right", 1.0f));
blendshapeMap.insert("LipsLowerDown", QPair<QByteArray, float>("Mix::LowerLipDown_Left", 0.5f));
blendshapeMap.insert("LipsLowerDown", QPair<QByteArray, float>("Mix::LowerLipDown_Right", 0.5f));
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::LowerLipIn", 1.0f));
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::LowerLipOut", 1.0f));
blendshapeMap.insert("MouthLeft", QPair<QByteArray, float>("Mix::Midmouth_Left", 1.0f));
blendshapeMap.insert("MouthRight", QPair<QByteArray, float>("Mix::Midmouth_Right", 1.0f));
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::MouthDown", 1.0f));
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::MouthNarrow_Left", 1.0f));
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::MouthNarrow_Right", 1.0f));
blendshapeMap.insert("JawOpen", QPair<QByteArray, float>("Mix::MouthOpen", 1.0f));
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::MouthUp", 1.0f));
blendshapeMap.insert("LipsPucker", QPair<QByteArray, float>("Mix::MouthWhistle_NarrowAdjust_Left", 0.5f));
blendshapeMap.insert("LipsPucker", QPair<QByteArray, float>("Mix::MouthWhistle_NarrowAdjust_Right", 0.5f));
blendshapeMap.insert("Sneer", QPair<QByteArray, float>("Mix::NoseScrunch_Left", 0.5f));
blendshapeMap.insert("Sneer", QPair<QByteArray, float>("Mix::NoseScrunch_Right", 0.5f));
blendshapeMap.insert("MouthSmile_L", QPair<QByteArray, float>("Mix::Smile_Left", 1.0f));
blendshapeMap.insert("MouthSmile_R", QPair<QByteArray, float>("Mix::Smile_Right", 1.0f));
blendshapeMap.insert("EyeSquint_L", QPair<QByteArray, float>("Mix::Squint_Left", 1.0f));
blendshapeMap.insert("EyeSquint_R", QPair<QByteArray, float>("Mix::Squint_Right", 1.0f));
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::UpperLipIn", 1.0f));
blendshapeMap.insert("...", QPair<QByteArray, float>("Mix::UpperLipOut", 1.0f));
blendshapeMap.insert("LipsUpperUp", QPair<QByteArray, float>("Mix::UpperLipUp_Left", 0.5f));
blendshapeMap.insert("LipsUpperUp", QPair<QByteArray, float>("Mix::UpperLipUp_Right", 0.5f));
QMultiHash<QByteArray, QPair<int, float> > channelNameMap;
for (int i = 0;; i++) {
QByteArray blendshape = FACESHIFT_BLENDSHAPES[i];
if (blendshape.isEmpty()) {
break;
}
for (QMultiHash<QByteArray, QPair<QByteArray, float> >::const_iterator it = blendshapeMap.constFind(blendshape);
it != blendshapeMap.constEnd() && it.key() == blendshape; it++) {
channelNameMap.insert(it.value().first, QPair<int, float>(i, it.value().second));
}
}
return channelNameMap;
}
static const QMultiHash<QByteArray, QPair<int, float> >& getChannelNameMap() {
static QMultiHash<QByteArray, QPair<int, float> > channelNameMap = createChannelNameMap();
return channelNameMap;
}
#endif
FaceplusReader::~FaceplusReader() {
#ifdef HAVE_FACEPLUS
if (faceplus_teardown()) {
qDebug() << "Faceplus torn down.";
}
#endif
}
void FaceplusReader::init() {
#ifdef HAVE_FACEPLUS
if (!faceplus_init("VGA")) {
qDebug() << "Failed to initialized Faceplus.";
return;
}
qDebug() << "Faceplus initialized.";
int channelCount = faceplus_output_channels_count();
_outputVector.resize(channelCount);
int maxIndex = -1;
_channelIndexMap.clear();
for (int i = 0; i < channelCount; i++) {
QByteArray name = faceplus_output_channel_name(i);
if (name == "Head_Joint::Rotation_X") {
_headRotationIndices[0] = i;
} else if (name == "Head_Joint::Rotation_Y") {
_headRotationIndices[1] = i;
} else if (name == "Head_Joint::Rotation_Z") {
_headRotationIndices[2] = i;
} else if (name == "Left_Eye_Joint::Rotation_X") {
_leftEyeRotationIndices[0] = i;
} else if (name == "Left_Eye_Joint::Rotation_Y") {
_leftEyeRotationIndices[1] = i;
} else if (name == "Right_Eye_Joint::Rotation_X") {
_rightEyeRotationIndices[0] = i;
} else if (name == "Right_Eye_Joint::Rotation_Y") {
_rightEyeRotationIndices[1] = i;
}
for (QMultiHash<QByteArray, QPair<int, float> >::const_iterator it = getChannelNameMap().constFind(name);
it != getChannelNameMap().constEnd() && it.key() == name; it++) {
_channelIndexMap.insert(i, it.value());
maxIndex = qMax(maxIndex, it.value().first);
}
}
_blendshapeCoefficients.resize(maxIndex + 1);
QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
#endif
}
void FaceplusReader::shutdown() {
deleteLater();
thread()->quit();
}
void FaceplusReader::update() {
#ifdef HAVE_FACEPLUS
if (!(faceplus_synchronous_track() && faceplus_current_output_vector(_outputVector.data()))) {
QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
return;
}
glm::quat headRotation(glm::radians(glm::vec3(-_outputVector.at(_headRotationIndices[0]),
_outputVector.at(_headRotationIndices[1]), -_outputVector.at(_headRotationIndices[2]))));
float estimatedEyePitch = (_outputVector.at(_leftEyeRotationIndices[0]) +
_outputVector.at(_rightEyeRotationIndices[0])) * -0.5f;
float estimatedEyeYaw = (_outputVector.at(_leftEyeRotationIndices[1]) +
_outputVector.at(_rightEyeRotationIndices[1])) * 0.5f;
qFill(_blendshapeCoefficients.begin(), _blendshapeCoefficients.end(), 0.0f);
for (int i = 0; i < _outputVector.size(); i++) {
for (QMultiHash<int, QPair<int, float> >::const_iterator it = _channelIndexMap.constFind(i);
it != _channelIndexMap.constEnd() && it.key() == i; it++) {
_blendshapeCoefficients[it.value().first] += _outputVector.at(i) * it.value().second;
}
}
QMetaObject::invokeMethod(Application::getInstance()->getFaceplus(), "setState", Q_ARG(const glm::quat&, headRotation),
Q_ARG(float, estimatedEyePitch), Q_ARG(float, estimatedEyeYaw), Q_ARG(const QVector<float>&, _blendshapeCoefficients));
QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
#endif
}

View file

@ -0,0 +1,79 @@
//
// Faceplus.h
// interface/src/devices
//
// Created by Andrzej Kapolka on 4/9/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_Faceplus_h
#define hifi_Faceplus_h
#include <QMultiHash>
#include <QPair>
#include <QVector>
#include "FaceTracker.h"
class FaceplusReader;
/// Interface for Mixamo FacePlus.
class Faceplus : public FaceTracker {
Q_OBJECT
public:
Faceplus();
virtual ~Faceplus();
void init();
bool isActive() const { return _active; }
Q_INVOKABLE void setState(const glm::quat& headRotation, float estimatedEyePitch, float estimatedEyeYaw,
const QVector<float>& blendshapeCoefficients);
public slots:
void updateEnabled();
private:
void setEnabled(bool enabled);
bool _enabled;
bool _active;
FaceplusReader* _reader;
};
Q_DECLARE_METATYPE(QVector<float>)
/// The reader object that lives in its own thread.
class FaceplusReader : public QObject {
Q_OBJECT
public:
virtual ~FaceplusReader();
Q_INVOKABLE void init();
Q_INVOKABLE void shutdown();
Q_INVOKABLE void update();
private:
#ifdef HAVE_FACEPLUS
QMultiHash<int, QPair<int, float> > _channelIndexMap;
QVector<float> _outputVector;
int _headRotationIndices[3];
int _leftEyeRotationIndices[2];
int _rightEyeRotationIndices[2];
QVector<float> _blendshapeCoefficients;
#endif
};
#endif // hifi_Faceplus_h

View file

@ -45,9 +45,7 @@ Faceshift::Faceshift() :
_jawOpenIndex(21),
_longTermAverageEyePitch(0.0f),
_longTermAverageEyeYaw(0.0f),
_longTermAverageInitialized(false),
_estimatedEyePitch(0.0f),
_estimatedEyeYaw(0.0f)
_longTermAverageInitialized(false)
{
connect(&_tcpSocket, SIGNAL(connected()), SLOT(noteConnected()));
connect(&_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(noteError(QAbstractSocket::SocketError)));

View file

@ -15,13 +15,12 @@
#include <QTcpSocket>
#include <QUdpSocket>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <fsbinarystream.h>
#include "FaceTracker.h"
/// Handles interaction with the Faceshift software, which provides head position/orientation and facial features.
class Faceshift : public QObject {
class Faceshift : public FaceTracker {
Q_OBJECT
public:
@ -34,9 +33,7 @@ public:
bool isActive() const;
const glm::quat& getHeadRotation() const { return _headRotation; }
const glm::vec3& getHeadAngularVelocity() const { return _headAngularVelocity; }
const glm::vec3& getHeadTranslation() const { return _headTranslation; }
// these pitch/yaw angles are in degrees
float getEyeGazeLeftPitch() const { return _eyeGazeLeftPitch; }
@ -45,11 +42,6 @@ public:
float getEyeGazeRightPitch() const { return _eyeGazeRightPitch; }
float getEyeGazeRightYaw() const { return _eyeGazeRightYaw; }
float getEstimatedEyePitch() const { return _estimatedEyePitch; }
float getEstimatedEyeYaw() const { return _estimatedEyeYaw; }
const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
float getLeftBlink() const { return getBlendshapeCoefficient(_leftBlinkIndex); }
float getRightBlink() const { return getBlendshapeCoefficient(_rightBlinkIndex); }
float getLeftEyeOpen() const { return getBlendshapeCoefficient(_leftEyeOpenIndex); }
@ -102,9 +94,7 @@ private:
bool _tracking;
quint64 _lastTrackingStateReceived;
glm::quat _headRotation;
glm::vec3 _headAngularVelocity;
glm::vec3 _headTranslation;
// degrees
float _eyeGazeLeftPitch;
@ -112,8 +102,6 @@ private:
float _eyeGazeRightPitch;
float _eyeGazeRightYaw;
QVector<float> _blendshapeCoefficients;
int _leftBlinkIndex;
int _rightBlinkIndex;
int _leftEyeOpenIndex;
@ -135,10 +123,6 @@ private:
float _longTermAverageEyePitch;
float _longTermAverageEyeYaw;
bool _longTermAverageInitialized;
// degrees
float _estimatedEyePitch;
float _estimatedEyeYaw;
};
#endif // hifi_Faceshift_h

View file

@ -30,7 +30,6 @@ int OculusManager::_scaleLocation;
int OculusManager::_scaleInLocation;
int OculusManager::_hmdWarpParamLocation;
bool OculusManager::_isConnected = false;
float OculusManager::_yawOffset = 0.0f; // radians
#ifdef HAVE_LIBOVR
using namespace OVR;
@ -190,18 +189,9 @@ void OculusManager::reset() {
#endif
}
void OculusManager::updateYawOffset() {
#ifdef HAVE_LIBOVR
float yaw, pitch, roll;
_sensorFusion->GetOrientation().GetEulerAngles<Axis_Y, Axis_X, Axis_Z, Rotate_CCW, Handed_R>(&yaw, &pitch, &roll);
_yawOffset = yaw;
#endif
}
void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) {
#ifdef HAVE_LIBOVR
_sensorFusion->GetOrientation().GetEulerAngles<Axis_Y, Axis_X, Axis_Z, Rotate_CCW, Handed_R>(&yaw, &pitch, &roll);
yaw = yaw - _yawOffset;
#endif
}

View file

@ -51,7 +51,6 @@ private:
static int _scaleInLocation;
static int _hmdWarpParamLocation;
static bool _isConnected;
static float _yawOffset;
#ifdef HAVE_LIBOVR
static OVR::Ptr<OVR::DeviceManager> _deviceManager;

View file

@ -37,9 +37,7 @@ const glm::vec3 DEFAULT_HEAD_ORIGIN(0.0f, 0.0f, 0.7f);
Visage::Visage() :
_enabled(false),
_active(false),
_headOrigin(DEFAULT_HEAD_ORIGIN),
_estimatedEyePitch(0.0f),
_estimatedEyeYaw(0.0f) {
_headOrigin(DEFAULT_HEAD_ORIGIN) {
#ifdef HAVE_VISAGE
QByteArray licensePath = Application::resourcesPath().toLatin1() + "visage/license.vlc";
@ -164,6 +162,7 @@ void Visage::reset() {
void Visage::updateEnabled() {
setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Visage) &&
!Menu::getInstance()->isOptionChecked(MenuOption::Faceplus) &&
!(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift) &&
Application::getInstance()->getFaceshift()->isConnectedOrConnecting()));
}

View file

@ -16,8 +16,7 @@
#include <QPair>
#include <QVector>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include "FaceTracker.h"
namespace VisageSDK {
class VisageTracker2;
@ -25,7 +24,7 @@ namespace VisageSDK {
}
/// Handles input from the Visage webcam feature tracking software.
class Visage : public QObject {
class Visage : public FaceTracker {
Q_OBJECT
public:
@ -37,14 +36,6 @@ public:
bool isActive() const { return _active; }
const glm::quat& getHeadRotation() const { return _headRotation; }
const glm::vec3& getHeadTranslation() const { return _headTranslation; }
float getEstimatedEyePitch() const { return _estimatedEyePitch; }
float getEstimatedEyeYaw() const { return _estimatedEyeYaw; }
const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
void update();
void reset();
@ -64,15 +55,8 @@ private:
bool _enabled;
bool _active;
glm::quat _headRotation;
glm::vec3 _headTranslation;
glm::vec3 _headOrigin;
float _estimatedEyePitch;
float _estimatedEyeYaw;
QVector<float> _blendshapeCoefficients;
};
#endif // hifi_Visage_h

View file

@ -1,192 +0,0 @@
//
// Oscilloscope.cpp
// interface/src/ui
//
// Created by Philip on 1/28/13.
// Copyright 2013 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 <limits>
#include <cstring>
#include <algorithm>
#include <stdint.h>
#include <QtCore/QDebug>
#include "InterfaceConfig.h"
#include "Oscilloscope.h"
// Reimplemented 4/26/13 (tosh) - don't blame Philip for bugs
using namespace std;
namespace { // everything in here only exists while compiling this .cpp file
// one sample buffer per channel
unsigned const MAX_SAMPLES = Oscilloscope::MAX_SAMPLES_PER_CHANNEL * Oscilloscope::MAX_CHANNELS;
// adding an x-coordinate yields twice the amount of vertices
unsigned const MAX_COORDS_PER_CHANNEL = Oscilloscope::MAX_SAMPLES_PER_CHANNEL * 2;
// allocated once for each channel
unsigned const MAX_COORDS = MAX_COORDS_PER_CHANNEL * Oscilloscope::MAX_CHANNELS;
// total amount of memory to allocate (in 16-bit integers)
unsigned const N_INT16_TO_ALLOC = MAX_SAMPLES + MAX_COORDS;
}
Oscilloscope::Oscilloscope(int w, int h, bool isEnabled) :
enabled(isEnabled),
inputPaused(false),
_width(w),
_height(h),
_samples(0l),
_vertices(0l),
// some filtering (see details in Log.h)
_lowPassCoeff(0.4f),
// three in -> one out
_downsampleRatio(3) {
// allocate enough space for the sample data and to turn it into
// vertices and since they're all 'short', do so in one shot
_samples = new short[N_INT16_TO_ALLOC];
memset(_samples, 0, N_INT16_TO_ALLOC * sizeof(short));
_vertices = _samples + MAX_SAMPLES;
// initialize write positions to start of each channel's region
for (unsigned ch = 0; ch < MAX_CHANNELS; ++ch) {
_writePos[ch] = MAX_SAMPLES_PER_CHANNEL * ch;
}
_colors[0] = 0xffffff;
_colors[1] = 0x00ffff;
_colors[2] = 0x00ffff;
}
Oscilloscope::~Oscilloscope() {
delete[] _samples;
}
void Oscilloscope::addSamples(const QByteArray& audioByteArray, bool isStereo, bool isInput) {
if (! enabled || inputPaused) {
return;
}
unsigned numSamplesPerChannel = audioByteArray.size() / (sizeof(int16_t) * (isStereo ? 2 : 1));
int16_t* samples = (int16_t*) audioByteArray.data();
for (int channel = 0; channel < (isStereo ? 2 : 1); channel++) {
// add samples for each of the channels
// determine start/end offset of this channel's region
unsigned baseOffs = MAX_SAMPLES_PER_CHANNEL * (channel + !isInput);
unsigned endOffs = baseOffs + MAX_SAMPLES_PER_CHANNEL;
// fetch write position for this channel
unsigned writePos = _writePos[channel + !isInput];
// determine write position after adding the samples
unsigned newWritePos = writePos + numSamplesPerChannel;
unsigned n2 = 0;
if (newWritePos >= endOffs) {
// passed boundary of the circular buffer? -> we need to copy two blocks
n2 = newWritePos - endOffs;
newWritePos = baseOffs + n2;
numSamplesPerChannel -= n2;
}
if (!isStereo) {
// copy data
memcpy(_samples + writePos, samples, numSamplesPerChannel * sizeof(int16_t));
if (n2 > 0) {
memcpy(_samples + baseOffs, samples + numSamplesPerChannel, n2 * sizeof(int16_t));
}
} else {
// we have interleaved samples we need to separate into two channels
for (unsigned i = 0; i < numSamplesPerChannel + n2; i++) {
if (i < numSamplesPerChannel - n2) {
_samples[writePos] = samples[(i * 2) + channel];
} else {
_samples[baseOffs] = samples[(i * 2) + channel];
}
}
}
// set new write position for this channel
_writePos[channel + !isInput] = newWritePos;
}
}
void Oscilloscope::render(int x, int y) {
if (! enabled) {
return;
}
// fetch low pass factor (and convert to fix point) / downsample factor
int lowPassFixPt = -(int)(std::numeric_limits<short>::min()) * _lowPassCoeff;
unsigned downsample = _downsampleRatio;
// keep half of the buffer for writing and ensure an even vertex count
unsigned usedWidth = min(_width, MAX_SAMPLES_PER_CHANNEL / (downsample * 2)) & ~1u;
unsigned usedSamples = usedWidth * downsample;
// expand samples to vertex data
for (unsigned ch = 0; ch < MAX_CHANNELS; ++ch) {
// for each channel: determine memory regions
short const* basePtr = _samples + MAX_SAMPLES_PER_CHANNEL * ch;
short const* endPtr = basePtr + MAX_SAMPLES_PER_CHANNEL;
short const* inPtr = _samples + _writePos[ch];
short* outPtr = _vertices + MAX_COORDS_PER_CHANNEL * ch;
int sample = 0, x = usedWidth;
for (int i = (int)usedSamples; --i >= 0 ;) {
if (inPtr == basePtr) {
// handle boundary, reading the circular sample buffer
inPtr = endPtr;
}
// read and (eventually) filter sample
sample += ((*--inPtr - sample) * lowPassFixPt) >> 15;
// write every nth as y with a corresponding x-coordinate
if (i % downsample == 0) {
*outPtr++ = short(--x);
*outPtr++ = short(sample);
}
}
}
// set up rendering state (vertex data lives at _vertices)
glLineWidth(1.0);
glDisable(GL_LINE_SMOOTH);
glPushMatrix();
glTranslatef((float)x + 0.0f, (float)y + _height / 2.0f, 0.0f);
glScaled(1.0f, _height / 32767.0f, 1.0f);
glVertexPointer(2, GL_SHORT, 0, _vertices);
glEnableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_INDEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
// render channel 0
glColor3ub(GLubyte(_colors[0] >> 16), GLubyte((_colors[0] >> 8) & 0xff), GLubyte(_colors[0] & 0xff));
glDrawArrays(GL_LINES, MAX_SAMPLES_PER_CHANNEL * 0, usedWidth);
// render channel 1
glColor3f(0.0f, 1.0f ,1.0f);
glColor3ub(GLubyte(_colors[1] >> 16), GLubyte((_colors[1] >> 8) & 0xff), GLubyte(_colors[1] & 0xff));
glDrawArrays(GL_LINES, MAX_SAMPLES_PER_CHANNEL * 1, usedWidth);
// render channel 2
glColor3ub(GLubyte(_colors[2] >> 16), GLubyte((_colors[2] >> 8) & 0xff), GLubyte(_colors[2] & 0xff));
glDrawArrays(GL_LINES, MAX_SAMPLES_PER_CHANNEL * 2, usedWidth);
// reset rendering state
glDisableClientState(GL_VERTEX_ARRAY);
glPopMatrix();
}

View file

@ -1,84 +0,0 @@
//
// Oscilloscope.h
// interface/src/ui
//
// Created by Philip on 1/28/13.
// Copyright 2013 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_Oscilloscope_h
#define hifi_Oscilloscope_h
#include <cassert>
#include <QtCore/QObject>
class Oscilloscope : public QObject {
Q_OBJECT
public:
Oscilloscope(int width, int height, bool isEnabled);
~Oscilloscope();
void render(int x, int y);
// Switches: On/Off, Stop Time
volatile bool enabled;
volatile bool inputPaused;
// Limits
static unsigned const MAX_CHANNELS = 3;
static unsigned const MAX_SAMPLES_PER_CHANNEL = 4096;
// Sets the color for a specific channel.
void setColor(unsigned ch, unsigned rgb) { assert(ch < MAX_CHANNELS); if (! inputPaused) { _colors[ch] = rgb; } }
// Controls a simple one pole IIR low pass filter that is provided to
// reduce high frequencies aliasing (to lower ones) when downsampling.
//
// The parameter sets the influence of the input in respect to the
// feed-back signal on the output.
//
// +---------+
// in O--------------|+ ideal |--o--------------O out
// .---|- op amp | |
// | +---------+ |
// | |
// o-------||-------o
// | |
// | __V__
// -------------|_____|-------+
// : : |
// 0.0 - 1.0 (GND)
//
// The values in range 0.0 - 1.0 correspond to "all closed" (input has
// no influence on the output) to "all open" (feedback has no influence
// on the output) configurations.
void setLowpassOpenness(float w) { assert(w >= 0.0f && w <= 1.0f); _lowPassCoeff = w; }
// Sets the number of input samples per output sample. Without filtering
// just uses every nTh sample.
void setDownsampleRatio(unsigned n) { assert(n > 0); _downsampleRatio = n; }
public slots:
void addSamples(const QByteArray& audioByteArray, bool isStereo, bool isInput);
private:
// don't copy/assign
Oscilloscope(Oscilloscope const&); // = delete;
Oscilloscope& operator=(Oscilloscope const&); // = delete;
// state variables
unsigned _width;
unsigned _height;
short* _samples;
short* _vertices;
unsigned _writePos[MAX_CHANNELS];
float _lowPassCoeff;
unsigned _downsampleRatio;
unsigned _colors[MAX_CHANNELS];
};
#endif // hifi_Oscilloscope_h