From f53b49c4966f0496cd641cc2d23f446e42716b63 Mon Sep 17 00:00:00 2001 From: Jeffrey Ventrella Date: Tue, 14 May 2013 17:30:41 -0700 Subject: [PATCH] working on lookat (eye contact) --- interface/src/Avatar.cpp | 116 +------------- interface/src/Avatar.h | 4 - interface/src/Head.cpp | 214 ++++++++++++++++++++------ interface/src/Head.h | 9 ++ interface/src/main.cpp | 2 +- libraries/avatars/src/Orientation.cpp | 9 ++ libraries/avatars/src/Orientation.h | 1 + 7 files changed, 193 insertions(+), 162 deletions(-) diff --git a/interface/src/Avatar.cpp b/interface/src/Avatar.cpp index 4b9a35733a..bd5e255437 100644 --- a/interface/src/Avatar.cpp +++ b/interface/src/Avatar.cpp @@ -293,7 +293,9 @@ bool Avatar::getIsNearInteractingOther() { void Avatar::simulate(float deltaTime) { //figure out if the mouse cursor is over any body spheres... - checkForMouseRayTouching(); + if (_isMine) { + checkForMouseRayTouching(); + } // update balls if (_balls) { _balls->simulate(deltaTime); } @@ -519,6 +521,9 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) { } if (_interactingOther) { + + _head.setLookatPosition( _interactingOther->getSpringyHeadPosition()); + _avatarTouch.setYourBodyPosition(_interactingOther->_position); _avatarTouch.setYourHandPosition(_interactingOther->_joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].springyPosition); _avatarTouch.setYourHandState (_interactingOther->_handState); @@ -592,115 +597,6 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) { } } -void Avatar::updateHead(float deltaTime) { - -/* - // Decay head back to center if turned on - if (_isMine && _returnHeadToCenter) { - // Decay back toward center - _headPitch *= (1.0f - DECAY * _head.returnSpringScale * 2 * deltaTime); - _headYaw *= (1.0f - DECAY * _head.returnSpringScale * 2 * deltaTime); - _headRoll *= (1.0f - DECAY * _head.returnSpringScale * 2 * deltaTime); - } - - // For invensense gyro, decay only slightly when roughly centered - if (_isMine) { - const float RETURN_RANGE = 15.0; - const float RETURN_STRENGTH = 2.0; - if (fabs(_headPitch) < RETURN_RANGE) { _headPitch *= (1.0f - RETURN_STRENGTH * deltaTime); } - if (fabs(_headYaw) < RETURN_RANGE) { _headYaw *= (1.0f - RETURN_STRENGTH * deltaTime); } - if (fabs(_headRoll) < RETURN_RANGE) { _headRoll *= (1.0f - RETURN_STRENGTH * deltaTime); } - } - - if (_head.noise) { - // Move toward new target - _headPitch += (_head.pitchTarget - _headPitch) * 10 * deltaTime; // (1.f - DECAY*deltaTime)*Pitch + ; - _headYaw += (_head.yawTarget - _headYaw ) * 10 * deltaTime; // (1.f - DECAY*deltaTime); - _headRoll *= 1.f - (DECAY * deltaTime); - } - - _head.leanForward *= (1.f - DECAY * 30 * deltaTime); - _head.leanSideways *= (1.f - DECAY * 30 * deltaTime); - - // Update where the avatar's eyes are - // - // First, decide if we are making eye contact or not - if (randFloat() < 0.005) { - _head.eyeContact = !_head.eyeContact; - _head.eyeContact = 1; - if (!_head.eyeContact) { - // If we just stopped making eye contact,move the eyes markedly away - _head.eyeballPitch[0] = _head.eyeballPitch[1] = _head.eyeballPitch[0] + 5.0 + (randFloat() - 0.5) * 10; - _head.eyeballYaw [0] = _head.eyeballYaw [1] = _head.eyeballYaw [0] + 5.0 + (randFloat() - 0.5) * 5; - } else { - // If now making eye contact, turn head to look right at viewer - SetNewHeadTarget(0,0); - } - } - - const float DEGREES_BETWEEN_VIEWER_EYES = 3; - const float DEGREES_TO_VIEWER_MOUTH = 7; - - if (_head.eyeContact) { - // Should we pick a new eye contact target? - if (randFloat() < 0.01) { - // Choose where to look next - if (randFloat() < 0.1) { - _head.eyeContactTarget = MOUTH; - } else { - if (randFloat() < 0.5) _head.eyeContactTarget = LEFT_EYE; else _head.eyeContactTarget = RIGHT_EYE; - } - } - // Set eyeball pitch and yaw to make contact - float eye_target_yaw_adjust = 0; - float eye_target_pitch_adjust = 0; - if (_head.eyeContactTarget == LEFT_EYE) eye_target_yaw_adjust = DEGREES_BETWEEN_VIEWER_EYES; - if (_head.eyeContactTarget == RIGHT_EYE) eye_target_yaw_adjust = -DEGREES_BETWEEN_VIEWER_EYES; - if (_head.eyeContactTarget == MOUTH) eye_target_pitch_adjust = DEGREES_TO_VIEWER_MOUTH; - - _head.eyeballPitch[0] = _head.eyeballPitch[1] = -_headPitch + eye_target_pitch_adjust; - _head.eyeballYaw[0] = _head.eyeballYaw[1] = -_headYaw + eye_target_yaw_adjust; - } - - if (_head.noise) - { - _headPitch += (randFloat() - 0.5) * 0.2 * _head.noiseEnvelope; - _headYaw += (randFloat() - 0.5) * 0.3 *_head.noiseEnvelope; - //PupilSize += (randFloat() - 0.5) * 0.001*NoiseEnvelope; - - if (randFloat() < 0.005) _head.mouthWidth = MouthWidthChoices[rand()%3]; - - if (!_head.eyeContact) { - if (randFloat() < 0.01) _head.eyeballPitch[0] = _head.eyeballPitch[1] = (randFloat() - 0.5) * 20; - if (randFloat() < 0.01) _head.eyeballYaw[0] = _head.eyeballYaw[1] = (randFloat()- 0.5) * 10; - } - - if ((randFloat() < 0.005) && (fabs(_head.pitchTarget - _headPitch) < 1.0) && (fabs(_head.yawTarget - _headYaw) < 1.0)) { - SetNewHeadTarget((randFloat()-0.5) * 20.0, (randFloat()-0.5) * 45.0); - } - - if (0) { - - // Pick new target - _head.pitchTarget = (randFloat() - 0.5) * 45; - _head.yawTarget = (randFloat() - 0.5) * 22; - } - if (randFloat() < 0.01) - { - _head.eyebrowPitch[0] = _head.eyebrowPitch[1] = BrowPitchAngle[rand()%3]; - _head.eyebrowRoll [0] = _head.eyebrowRoll[1] = BrowRollAngle[rand()%5]; - _head.eyebrowRoll [1] *=-1; - } - } - - // Update audio trailing average for rendering facial animations - const float AUDIO_AVERAGING_SECS = 0.05; - _head.averageLoudness = (1.f - deltaTime / AUDIO_AVERAGING_SECS) * _head.averageLoudness + - (deltaTime / AUDIO_AVERAGING_SECS) * _audioLoudness; -*/ -} - - float Avatar::getHeight() { return _height; } diff --git a/interface/src/Avatar.h b/interface/src/Avatar.h index beca93b53e..c922098f59 100644 --- a/interface/src/Avatar.h +++ b/interface/src/Avatar.h @@ -195,9 +195,6 @@ private: glm::vec3 _mouseRayDirection; glm::vec3 _cameraPosition; - //AvatarJointID _jointTouched; - - // private methods... void initializeSkeleton(); void updateSkeleton(); @@ -205,7 +202,6 @@ private: void updateBodySprings( float deltaTime ); void calculateBoneLengths(); void readSensors(); - void updateHead( float deltaTime ); void updateHandMovementAndTouching(float deltaTime); void updateAvatarCollisions(float deltaTime); void updateCollisionWithSphere( glm::vec3 position, float radius, float deltaTime ); diff --git a/interface/src/Head.cpp b/interface/src/Head.cpp index 51d3e42c3f..39875873db 100644 --- a/interface/src/Head.cpp +++ b/interface/src/Head.cpp @@ -7,6 +7,7 @@ // #include "Head.h" +#include "Util.h" #include #include #include @@ -15,6 +16,8 @@ using namespace std; const float HEAD_MOTION_DECAY = 0.1; +const bool TESTING_LOOKAT = false; + float _browColor [] = {210.0/255.0, 105.0/255.0, 30.0/255.0}; float _mouthColor[] = {1, 0, 0}; @@ -49,6 +52,7 @@ void Head::initialize() { audioLoudness = 0.0; skinColor = glm::vec3(0.0f, 0.0f, 0.0f); position = glm::vec3(0.0f, 0.0f, 0.0f); + lookatPosition = glm::vec3(0.0f, 0.0f, 0.0f); yaw = 0.0f; pitch = 0.0f; roll = 0.0f; @@ -219,11 +223,21 @@ void Head::simulate(float deltaTime, bool isMine) { const float AUDIO_AVERAGING_SECS = 0.05; averageLoudness = (1.f - deltaTime / AUDIO_AVERAGING_SECS) * averageLoudness + (deltaTime / AUDIO_AVERAGING_SECS) * audioLoudness; + } +void Head::setLookatPosition(glm::vec3 l) { + lookatPosition = l; +} + + + void Head::render(bool lookingInMirror, float bodyYaw) { + +//float a = angleBetween( &position, &position); + int side = 0; glEnable(GL_DEPTH_TEST); @@ -236,11 +250,11 @@ void Head::render(bool lookingInMirror, float bodyYaw) { glScalef(scale, scale, scale); if (lookingInMirror) { - glRotatef(bodyYaw - yaw, 0, 1, 0); + glRotatef(bodyYaw - yaw, 0, 1, 0); glRotatef(pitch, 1, 0, 0); glRotatef(-roll, 0, 0, 1); } else { - glRotatef(bodyYaw + yaw, 0, 1, 0); + glRotatef(bodyYaw + yaw, 0, 1, 0); glRotatef(pitch, 1, 0, 0); glRotatef(roll, 0, 0, 1); } @@ -290,24 +304,37 @@ void Head::render(bool lookingInMirror, float bodyYaw) { // Mouth glPushMatrix(); - glTranslatef(0,-0.35,0.75); - glColor3f(0,0,0); + glTranslatef(0,-0.35,0.75); + glColor3f(0,0,0); - glRotatef(mouthPitch, 1, 0, 0); - glRotatef(mouthYaw, 0, 0, 1); + glRotatef(mouthPitch, 1, 0, 0); + glRotatef(mouthYaw, 0, 0, 1); - if (averageLoudness > 1.f) { - glScalef(mouthWidth * (.7f + sqrt(averageLoudness) /60.f), - mouthHeight * (1.f + sqrt(averageLoudness) /30.f), 1); - } else { - glScalef(mouthWidth, mouthHeight, 1); - } + if (averageLoudness > 1.f) { + glScalef(mouthWidth * (.7f + sqrt(averageLoudness) /60.f), + mouthHeight * (1.f + sqrt(averageLoudness) /30.f), 1); + } else { + glScalef(mouthWidth, mouthHeight, 1); + } - glutSolidCube(0.5); + glutSolidCube(0.5); glPopMatrix(); - glTranslatef(0, 1.0, 0); + renderEyeBalls(); + glPopMatrix(); + + if (TESTING_LOOKAT) { + //the irises are special - they have the ability to look at specific targets in the world (code still not finished yet) + renderIrises(bodyYaw + yaw); + } +} + + + +void Head::renderEyeBalls() { + + glTranslatef(0, 1.0, 0); glTranslatef(-interPupilDistance/2.0,-0.68,0.7); // Right Eye @@ -322,30 +349,33 @@ void Head::render(bool lookingInMirror, float bodyYaw) { } glPopMatrix(); - // Right Pupil - if (sphere == NULL) { - sphere = gluNewQuadric(); - gluQuadricTexture(sphere, GL_TRUE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gluQuadricOrientation(sphere, GLU_OUTSIDE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, iris_texture_width, iris_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &iris_texture[0]); + if (!TESTING_LOOKAT) { + // Right Pupil + if (sphere == NULL) { + sphere = gluNewQuadric(); + gluQuadricTexture(sphere, GL_TRUE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gluQuadricOrientation(sphere, GLU_OUTSIDE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, iris_texture_width, iris_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &iris_texture[0]); + } + + glPushMatrix(); + { + glRotatef(eyeballPitch[1], 1, 0, 0); + glRotatef(eyeballYaw[1] + yaw + pupilConverge, 0, 1, 0); + glTranslatef(0,0,.35); + glRotatef(-75,1,0,0); + glScalef(1.0, 0.4, 1.0); + + glEnable(GL_TEXTURE_2D); + gluSphere(sphere, pupilSize, 15, 15); + glDisable(GL_TEXTURE_2D); + } + glPopMatrix(); } - - glPushMatrix(); - { - glRotatef(eyeballPitch[1], 1, 0, 0); - glRotatef(eyeballYaw[1] + yaw + pupilConverge, 0, 1, 0); - glTranslatef(0,0,.35); - glRotatef(-75,1,0,0); - glScalef(1.0, 0.4, 1.0); - - glEnable(GL_TEXTURE_2D); - gluSphere(sphere, pupilSize, 15, 15); - glDisable(GL_TEXTURE_2D); - } - - glPopMatrix(); + + // Left Eye glColor3fv(_eyeColor); glTranslatef(interPupilDistance, 0, 0); @@ -357,21 +387,111 @@ void Head::render(bool lookingInMirror, float bodyYaw) { glutSolidSphere(0.25, 30, 30); } glPopMatrix(); - // Left Pupil + + if (!TESTING_LOOKAT) { + // Left Pupil + glPushMatrix(); + { + glRotatef(eyeballPitch[0], 1, 0, 0); + glRotatef(eyeballYaw[0] + yaw - pupilConverge, 0, 1, 0); + glTranslatef(0, 0, .35); + glRotatef(-75, 1, 0, 0); + glScalef(1.0, 0.4, 1.0); + + glEnable(GL_TEXTURE_2D); + gluSphere(sphere, pupilSize, 15, 15); + glDisable(GL_TEXTURE_2D); + } + glPopMatrix(); + } +} + + +//--------------------------------------------------------- +// the code below is still in the prototyping stages.... +//--------------------------------------------------------- +void Head::renderIrises(float yaw) { + Orientation orientation; + orientation.setToPitchYawRoll( pitch, yaw, roll); + + // Render lines originating from the eyes and converging on the lookatPosition + float rightShift = scale * 0.22f; + float upShift = scale * 0.38f; + float frontShift = scale * 0.9f; + + glm::vec3 leftEyePosition = position + orientation.getRight() * rightShift + + orientation.getUp () * upShift + + orientation.getFront() * frontShift; + glm::vec3 rightEyePosition = position - orientation.getRight() * rightShift + + orientation.getUp () * upShift + + orientation.getFront() * frontShift; + + debugRenderLookatVectors(leftEyePosition, rightEyePosition, lookatPosition); + + glColor3fv(_eyeColor); + + if (sphere == NULL) { + sphere = gluNewQuadric(); + gluQuadricTexture(sphere, GL_TRUE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gluQuadricOrientation(sphere, GLU_OUTSIDE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, iris_texture_width, iris_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &iris_texture[0]); + } + + //render left iris glPushMatrix(); { - glRotatef(eyeballPitch[0], 1, 0, 0); - glRotatef(eyeballYaw[0] + yaw - pupilConverge, 0, 1, 0); - glTranslatef(0, 0, .35); - glRotatef(-75, 1, 0, 0); - glScalef(1.0, 0.4, 1.0); + glTranslatef(leftEyePosition.x, leftEyePosition.y, leftEyePosition.z); + glm::vec3 targetLookatAxis = glm::normalize(lookatPosition - leftEyePosition); - glEnable(GL_TEXTURE_2D); - gluSphere(sphere, pupilSize, 15, 15); - glDisable(GL_TEXTURE_2D); + glPushMatrix(); + glm::vec3 pitchRotationAxis = glm::cross(targetLookatAxis, orientation.getRight()); + glm::vec3 yawRotationAxis = glm::cross(targetLookatAxis, orientation.getUp()); + + glRotatef(90.0f, yawRotationAxis.x, yawRotationAxis.y, yawRotationAxis.z); + glRotatef(90.0f, pitchRotationAxis.x, pitchRotationAxis.y, pitchRotationAxis.z); + glEnable(GL_TEXTURE_2D); + gluSphere(sphere, 0.01, 15, 15); + glDisable(GL_TEXTURE_2D); + glPopMatrix(); } glPopMatrix(); - + + //render right iris + glPushMatrix(); + { + glTranslatef(rightEyePosition.x, rightEyePosition.y, rightEyePosition.z); + glm::vec3 targetLookatAxis = glm::normalize(lookatPosition - rightEyePosition); + + glPushMatrix(); + glm::vec3 pitchRotationAxis = glm::cross(targetLookatAxis, orientation.getRight()); + glm::vec3 yawRotationAxis = glm::cross(targetLookatAxis, orientation.getUp()); + + glRotatef(90.0f, yawRotationAxis.x, yawRotationAxis.y, yawRotationAxis.z); + glRotatef(90.0f, pitchRotationAxis.x, pitchRotationAxis.y, pitchRotationAxis.z); + glEnable(GL_TEXTURE_2D); + gluSphere(sphere, 0.01, 15, 15); + glDisable(GL_TEXTURE_2D); + glPopMatrix(); + } glPopMatrix(); } + +void Head::debugRenderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) { + + glColor3f(0.0f, 0.0f, 0.0f); + glLineWidth(3.0); + glBegin(GL_LINE_STRIP); + glVertex3f(leftEyePosition.x, leftEyePosition.y, leftEyePosition.z); + glVertex3f(lookatPosition.x, lookatPosition.y, lookatPosition.z); + glEnd(); + glBegin(GL_LINE_STRIP); + glVertex3f(rightEyePosition.x, rightEyePosition.y, rightEyePosition.z); + glVertex3f(lookatPosition.x, lookatPosition.y, lookatPosition.z); + glEnd(); +} + + + diff --git a/interface/src/Head.h b/interface/src/Head.h index ee12098e24..4562f82ad8 100644 --- a/interface/src/Head.h +++ b/interface/src/Head.h @@ -15,6 +15,7 @@ #include "world.h" #include "InterfaceConfig.h" #include "SerialInterface.h" +#include "Orientation.h" enum eyeContactTargets {LEFT_EYE, RIGHT_EYE, MOUTH}; @@ -30,6 +31,7 @@ public: void render(bool lookingInMirror, float bodyYaw); void setNewTarget(float, float); void setSpringScale(float s) { returnSpringScale = s; } + void setLookatPosition(glm::vec3 lookatPosition); // Do you want head to try to return to center (depends on interface detected) void setReturnToCenter(bool r) { returnHeadToCenter = r; } @@ -43,6 +45,7 @@ public: glm::vec3 skinColor; glm::vec3 position; glm::vec3 rotation; + glm::vec3 lookatPosition; float yaw; float pitch; float roll; @@ -85,6 +88,12 @@ public: // Strength of return springs float returnSpringScale; + +private: + void renderEyeBalls(); + void renderIrises(float yaw); + void debugRenderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition); + }; #endif diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 2fd5ef7d3a..54ed1a58b8 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -1059,6 +1059,7 @@ void display(void) myCamera.setTargetRotation(myAvatar.getBodyYaw() + myAvatar.getHeadYaw(), -myAvatar.getHeadPitch(), myAvatar.getHeadRoll()); } else if (myCamera.getMode() == CAMERA_MODE_MIRROR) { + myCamera.setTightness (100.0f); myCamera.setTargetPosition(myAvatar.getSpringyHeadPosition()); myCamera.setTargetRotation(myAvatar.getBodyYaw() - 180.0f, 0.0f, 0.0f); @@ -1086,7 +1087,6 @@ void display(void) glPopMatrix(); */ - // Note: whichCamera is used to pick between the normal camera myCamera for our // main camera, vs, an alternate camera. The alternate camera we support right now // is the viewFrustumOffsetCamera. But theoretically, we could use this same mechanism diff --git a/libraries/avatars/src/Orientation.cpp b/libraries/avatars/src/Orientation.cpp index 447739c32c..1f74cb7f80 100755 --- a/libraries/avatars/src/Orientation.cpp +++ b/libraries/avatars/src/Orientation.cpp @@ -25,6 +25,15 @@ void Orientation::setToIdentity() { front = glm::vec3(IDENTITY_FRONT); } +void Orientation::setToPitchYawRoll(float p, float y, float r) { + + setToIdentity(); + pitch(p); + yaw (y); + roll (r); +} + + void Orientation::set(Orientation o) { quat = o.quat; diff --git a/libraries/avatars/src/Orientation.h b/libraries/avatars/src/Orientation.h index 4bf487be02..ea1dbf16ac 100755 --- a/libraries/avatars/src/Orientation.h +++ b/libraries/avatars/src/Orientation.h @@ -22,6 +22,7 @@ public: Orientation(); void set(Orientation); + void setToPitchYawRoll(float p, float y, float r); void setToIdentity(); void pitch(float p);