Merge pull request #3488 from PhilipRosedale/master

Correct eye contact for Oculus users
This commit is contained in:
Stephen Birarda 2014-09-26 15:42:03 -07:00
commit 7c738edbd8
8 changed files with 102 additions and 47 deletions

View file

@ -1997,8 +1997,16 @@ void Application::updateMyAvatarLookAtPosition() {
glm::vec3 lookAtSpot; glm::vec3 lookAtSpot;
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
// When I am in mirror mode, just look right at the camera (myself) // When I am in mirror mode, just look right at the camera (myself)
lookAtSpot = _myCamera.getPosition(); if (!OculusManager::isConnected()) {
lookAtSpot = _myCamera.getPosition();
} else {
if (_myAvatar->isLookingAtLeftEye()) {
lookAtSpot = OculusManager::getLeftEyePosition();
} else {
lookAtSpot = OculusManager::getRightEyePosition();
}
}
} else { } else {
AvatarSharedPointer lookingAt = _myAvatar->getLookAtTargetAvatar().toStrongRef(); AvatarSharedPointer lookingAt = _myAvatar->getLookAtTargetAvatar().toStrongRef();
if (lookingAt && _myAvatar != lookingAt.data()) { if (lookingAt && _myAvatar != lookingAt.data()) {
@ -2006,7 +2014,7 @@ void Application::updateMyAvatarLookAtPosition() {
isLookingAtSomeone = true; isLookingAtSomeone = true;
// If I am looking at someone else, look directly at one of their eyes // If I am looking at someone else, look directly at one of their eyes
if (tracker) { if (tracker) {
// If tracker active, look at the eye for the side my gaze is biased toward // If a face tracker is active, look at the eye for the side my gaze is biased toward
if (tracker->getEstimatedEyeYaw() > _myAvatar->getHead()->getFinalYaw()) { if (tracker->getEstimatedEyeYaw() > _myAvatar->getHead()->getFinalYaw()) {
// Look at their right eye // Look at their right eye
lookAtSpot = static_cast<Avatar*>(lookingAt.data())->getHead()->getRightEyePosition(); lookAtSpot = static_cast<Avatar*>(lookingAt.data())->getHead()->getRightEyePosition();
@ -2016,7 +2024,11 @@ void Application::updateMyAvatarLookAtPosition() {
} }
} else { } else {
// Need to add randomly looking back and forth between left and right eye for case with no tracker // Need to add randomly looking back and forth between left and right eye for case with no tracker
lookAtSpot = static_cast<Avatar*>(lookingAt.data())->getHead()->getEyePosition(); if (_myAvatar->isLookingAtLeftEye()) {
lookAtSpot = static_cast<Avatar*>(lookingAt.data())->getHead()->getLeftEyePosition();
} else {
lookAtSpot = static_cast<Avatar*>(lookingAt.data())->getHead()->getRightEyePosition();
}
} }
} else { } else {
// I am not looking at anyone else, so just look forward // I am not looking at anyone else, so just look forward

View file

@ -110,7 +110,7 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
bool forceBlink = false; bool forceBlink = false;
const float TALKING_LOUDNESS = 100.0f; const float TALKING_LOUDNESS = 100.0f;
const float BLINK_AFTER_TALKING = 0.25f; const float BLINK_AFTER_TALKING = 0.25f;
if (_averageLoudness > TALKING_LOUDNESS) { if ((_averageLoudness - _longTermAverageLoudness) > TALKING_LOUDNESS) {
_timeWithoutTalking = 0.0f; _timeWithoutTalking = 0.0f;
} else if (_timeWithoutTalking < BLINK_AFTER_TALKING && (_timeWithoutTalking += deltaTime) >= BLINK_AFTER_TALKING) { } else if (_timeWithoutTalking < BLINK_AFTER_TALKING && (_timeWithoutTalking += deltaTime) >= BLINK_AFTER_TALKING) {
@ -118,7 +118,8 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
} }
// Update audio attack data for facial animation (eyebrows and mouth) // Update audio attack data for facial animation (eyebrows and mouth)
_audioAttack = 0.9f * _audioAttack + 0.1f * fabs((_audioLoudness - _longTermAverageLoudness) - _lastLoudness); const float AUDIO_ATTACK_AVERAGING_RATE = 0.9f;
_audioAttack = AUDIO_ATTACK_AVERAGING_RATE * _audioAttack + (1.0f - AUDIO_ATTACK_AVERAGING_RATE) * fabs((_audioLoudness - _longTermAverageLoudness) - _lastLoudness);
_lastLoudness = (_audioLoudness - _longTermAverageLoudness); _lastLoudness = (_audioLoudness - _longTermAverageLoudness);
const float BROW_LIFT_THRESHOLD = 100.0f; const float BROW_LIFT_THRESHOLD = 100.0f;
@ -128,16 +129,23 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
_browAudioLift = glm::clamp(_browAudioLift *= 0.7f, 0.0f, 1.0f); _browAudioLift = glm::clamp(_browAudioLift *= 0.7f, 0.0f, 1.0f);
const float BLINK_SPEED = 10.0f; const float BLINK_SPEED = 10.0f;
const float BLINK_SPEED_VARIABILITY = 1.0f;
const float BLINK_START_VARIABILITY = 0.25f;
const float FULLY_OPEN = 0.0f; const float FULLY_OPEN = 0.0f;
const float FULLY_CLOSED = 1.0f; const float FULLY_CLOSED = 1.0f;
if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) { if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) {
// no blinking when brows are raised; blink less with increasing loudness // no blinking when brows are raised; blink less with increasing loudness
const float BASE_BLINK_RATE = 15.0f / 60.0f; const float BASE_BLINK_RATE = 15.0f / 60.0f;
const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f; const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f;
if (forceBlink || (_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(_averageLoudness) * if (forceBlink || (_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(fabs(_averageLoudness - _longTermAverageLoudness)) *
ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) { ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) {
_leftEyeBlinkVelocity = BLINK_SPEED; _leftEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY;
_rightEyeBlinkVelocity = BLINK_SPEED; _rightEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY;
if (randFloat() < 0.5f) {
_leftEyeBlink = BLINK_START_VARIABILITY;
} else {
_rightEyeBlink = BLINK_START_VARIABILITY;
}
} }
} else { } else {
_leftEyeBlink = glm::clamp(_leftEyeBlink + _leftEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); _leftEyeBlink = glm::clamp(_leftEyeBlink + _leftEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED);

View file

@ -86,7 +86,8 @@ MyAvatar::MyAvatar() :
_shouldRender(true), _shouldRender(true),
_billboardValid(false), _billboardValid(false),
_physicsSimulation(), _physicsSimulation(),
_voxelShapeManager() _voxelShapeManager(),
_isLookingAtLeftEye(true)
{ {
ShapeCollider::initDispatchTable(); ShapeCollider::initDispatchTable();
for (int i = 0; i < MAX_DRIVE_KEYS; i++) { for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
@ -978,7 +979,11 @@ void MyAvatar::updateLookAtTargetAvatar() {
howManyLookingAtMe++; howManyLookingAtMe++;
// Have that avatar look directly at my camera // Have that avatar look directly at my camera
// Philip TODO: correct to look at left/right eye // Philip TODO: correct to look at left/right eye
avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition()); if (OculusManager::isConnected()) {
avatar->getHead()->setCorrectedLookAtPosition(OculusManager::getLeftEyePosition());
} else {
avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition());
}
} else { } else {
avatar->getHead()->clearCorrectedLookAtPosition(); avatar->getHead()->clearCorrectedLookAtPosition();
} }
@ -993,6 +998,14 @@ void MyAvatar::clearLookAtTargetAvatar() {
_lookAtTargetAvatar.clear(); _lookAtTargetAvatar.clear();
} }
bool MyAvatar::isLookingAtLeftEye() {
float const CHANCE_OF_CHANGING_EYE = 0.01f;
if (randFloat() < CHANCE_OF_CHANGING_EYE) {
_isLookingAtLeftEye = !_isLookingAtLeftEye;
}
return _isLookingAtLeftEye;
}
glm::vec3 MyAvatar::getUprightHeadPosition() const { glm::vec3 MyAvatar::getUprightHeadPosition() const {
return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, getPelvisToHeadLength(), 0.0f); return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, getPelvisToHeadLength(), 0.0f);
} }

View file

@ -106,6 +106,8 @@ public:
void jump() { _shouldJump = true; }; void jump() { _shouldJump = true; };
bool isMyAvatar() { return true; } bool isMyAvatar() { return true; }
bool isLookingAtLeftEye();
virtual int parseDataAtOffset(const QByteArray& packet, int offset); virtual int parseDataAtOffset(const QByteArray& packet, int offset);
@ -230,6 +232,8 @@ private:
QList<AnimationHandlePointer> _animationHandles; QList<AnimationHandlePointer> _animationHandles;
PhysicsSimulation _physicsSimulation; PhysicsSimulation _physicsSimulation;
VoxelShapeManager _voxelShapeManager; VoxelShapeManager _voxelShapeManager;
bool _isLookingAtLeftEye;
RecorderPointer _recorder; RecorderPointer _recorder;

View file

@ -55,6 +55,9 @@ bool OculusManager::_programInitialized = false;
Camera* OculusManager::_camera = NULL; Camera* OculusManager::_camera = NULL;
int OculusManager::_activeEyeIndex = -1; int OculusManager::_activeEyeIndex = -1;
glm::vec3 OculusManager::_leftEyePosition;
glm::vec3 OculusManager::_rightEyePosition;
#endif #endif
void OculusManager::connect() { void OculusManager::connect() {
@ -348,6 +351,17 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p
_camera->setTargetRotation(bodyOrientation * orientation); _camera->setTargetRotation(bodyOrientation * orientation);
_camera->setTargetPosition(position + trackerPosition); _camera->setTargetPosition(position + trackerPosition);
// Store the latest left and right eye render locations for things that need to know
glm::vec3 thisEyePosition = position + trackerPosition +
(bodyOrientation * glm::quat(orientation.x, orientation.y, orientation.z, orientation.w) *
glm::vec3(_eyeRenderDesc[eye].ViewAdjust.x, _eyeRenderDesc[eye].ViewAdjust.y, _eyeRenderDesc[eye].ViewAdjust.z));
if (eyeIndex == 0) {
_leftEyePosition = thisEyePosition;
} else {
_rightEyePosition = thisEyePosition;
}
_camera->update(1.0f / Application::getInstance()->getFps()); _camera->update(1.0f / Application::getInstance()->getFps());
Matrix4f proj = ovrMatrix4f_Projection(_eyeRenderDesc[eye].Fov, whichCamera.getNearClip(), whichCamera.getFarClip(), true); Matrix4f proj = ovrMatrix4f_Projection(_eyeRenderDesc[eye].Fov, whichCamera.getNearClip(), whichCamera.getFarClip(), true);
@ -362,7 +376,7 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); glLoadIdentity();
glTranslatef(_eyeRenderDesc[eye].ViewAdjust.x, _eyeRenderDesc[eye].ViewAdjust.y, _eyeRenderDesc[eye].ViewAdjust.z); glTranslatef(_eyeRenderDesc[eye].ViewAdjust.x, _eyeRenderDesc[eye].ViewAdjust.y, _eyeRenderDesc[eye].ViewAdjust.z);
Application::getInstance()->displaySide(*_camera); Application::getInstance()->displaySide(*_camera);
applicationOverlay.displayOverlayTextureOculus(*_camera); applicationOverlay.displayOverlayTextureOculus(*_camera);

View file

@ -45,7 +45,16 @@ public:
static void overrideOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal, static void overrideOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane); float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane);
#ifdef HAVE_LIBOVR
static glm::vec3 getLeftEyePosition() { return _leftEyePosition; }
static glm::vec3 getRightEyePosition() { return _rightEyePosition; }
#else
static glm::vec3 getLeftEyePosition() { return glm::vec3(); }
static glm::vec3 getRightEyePosition() { return glm::vec3(); }
#endif
private: private:
#ifdef HAVE_LIBOVR #ifdef HAVE_LIBOVR
static void generateDistortionMesh(); static void generateDistortionMesh();
@ -96,7 +105,12 @@ private:
static bool _programInitialized; static bool _programInitialized;
static Camera* _camera; static Camera* _camera;
static int _activeEyeIndex; static int _activeEyeIndex;
static glm::vec3 _leftEyePosition;
static glm::vec3 _rightEyePosition;
#endif #endif
}; };
#endif // hifi_OculusManager_h #endif // hifi_OculusManager_h

View file

@ -13,50 +13,40 @@
#include <cmath> #include <cmath>
#include "StdDev.h" #include "StdDev.h"
const int MAX_STDEV_SAMPLES = 1000; const int NUM_SAMPLES = 1000;
StDev::StDev() { StDev::StDev() {
data = new float[MAX_STDEV_SAMPLES]; _data = new float[NUM_SAMPLES];
sampleCount = 0; _sampleCount = 0;
} }
void StDev::reset() { void StDev::reset() {
sampleCount = 0; _sampleCount = 0;
} }
void StDev::addValue(float v) { void StDev::addValue(float v) {
data[sampleCount++] = v; _data[_sampleCount++] = v;
if (sampleCount == MAX_STDEV_SAMPLES) sampleCount = 0; if (_sampleCount == NUM_SAMPLES) _sampleCount = 0;
} }
float StDev::getAverage() const { float StDev::getAverage() const {
float average = 0; float average = 0;
for (int i = 0; i < sampleCount; i++) { for (int i = 0; i < _sampleCount; i++) {
average += data[i]; average += _data[i];
} }
if (sampleCount > 0) if (_sampleCount > 0)
return average/(float)sampleCount; return average / (float)_sampleCount;
else return 0; else return 0;
} }
/*
float StDev::getMax() {
float average = -FLT_MAX;
for (int i = 0; i < sampleCount; i++) {
average += data[i];
}
if (sampleCount > 0)
return average/(float)sampleCount;
else return 0;
}*/
float StDev::getStDev() const { float StDev::getStDev() const {
float average = getAverage(); float average = getAverage();
float stdev = 0; float stdev = 0;
for (int i = 0; i < sampleCount; i++) { for (int i = 0; i < _sampleCount; i++) {
stdev += powf(data[i] - average, 2); stdev += powf(_data[i] - average, 2);
} }
if (sampleCount > 1) if (_sampleCount > 1)
return sqrt(stdev/(float)(sampleCount - 1.0)); return sqrt(stdev / (float)(_sampleCount - 1.0f));
else else
return 0; return 0;
} }

View file

@ -13,16 +13,16 @@
#define hifi_StdDev_h #define hifi_StdDev_h
class StDev { class StDev {
public: public:
StDev(); StDev();
void reset(); void reset();
void addValue(float v); void addValue(float v);
float getAverage() const; float getAverage() const;
float getStDev() const; float getStDev() const;
int getSamples() const { return sampleCount; } int getSamples() const { return _sampleCount; }
private: private:
float * data; float* _data;
int sampleCount; int _sampleCount;
}; };
#endif // hifi_StdDev_h #endif // hifi_StdDev_h