Merge pull request #16317 from luiscuenca/setAvatarLookAtAPI

DEV-2285: Improve eyes look at
This commit is contained in:
Anthony Thibault 2019-10-23 20:42:27 -07:00 committed by GitHub
commit edb181bd16
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 103 additions and 18 deletions

View file

@ -5845,14 +5845,14 @@ void Application::pushPostUpdateLambda(void* key, const std::function<void()>& f
// to everyone.
// The principal result is to call updateLookAtTargetAvatar() and then setLookAtPosition().
// Note that it is called BEFORE we update position or joints based on sensors, etc.
void Application::updateMyAvatarLookAtPosition() {
void Application::updateMyAvatarLookAtPosition(float deltaTime) {
PerformanceTimer perfTimer("lookAt");
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()");
auto myAvatar = getMyAvatar();
FaceTracker* faceTracker = getActiveFaceTracker();
myAvatar->updateLookAtPosition(faceTracker, _myCamera);
myAvatar->updateEyesLookAtPosition(faceTracker, _myCamera, deltaTime);
}
void Application::updateThreads(float deltaTime) {
@ -6636,7 +6636,7 @@ void Application::update(float deltaTime) {
{
PROFILE_RANGE(simulation, "MyAvatar");
PerformanceTimer perfTimer("MyAvatar");
qApp->updateMyAvatarLookAtPosition();
qApp->updateMyAvatarLookAtPosition(deltaTime);
avatarManager->updateMyAvatar(deltaTime);
}
}

View file

@ -292,7 +292,7 @@ public:
virtual void pushPostUpdateLambda(void* key, const std::function<void()>& func) override;
void updateMyAvatarLookAtPosition();
void updateMyAvatarLookAtPosition(float deltaTime);
float getGameLoopRate() const { return _gameLoopCounter.rate(); }

View file

@ -2158,7 +2158,7 @@ static float lookAtCostFunction(const glm::vec3& myForward, const glm::vec3& myP
const float DISTANCE_FACTOR = 3.14f;
const float MY_ANGLE_FACTOR = 1.0f;
const float OTHER_ANGLE_FACTOR = 1.0f;
const float OTHER_IS_TALKING_TERM = otherIsTalking ? 1.0f : 0.0f;
const float OTHER_IS_TALKING_TERM = otherIsTalking ? -1.0f : 0.0f;
const float LOOKING_AT_OTHER_ALREADY_TERM = lookingAtOtherAlready ? -0.2f : 0.0f;
const float GREATEST_LOOKING_AT_DISTANCE = 10.0f; // meters
@ -2184,6 +2184,9 @@ static float lookAtCostFunction(const glm::vec3& myForward, const glm::vec3& myP
void MyAvatar::computeMyLookAtTarget(const AvatarHash& hash) {
glm::vec3 myForward = _lookAtYaw * IDENTITY_FORWARD;
if (_skeletonModel->isLoaded()) {
myForward = getHeadJointFrontVector();
}
glm::vec3 myPosition = getHead()->getEyePosition();
CameraMode mode = qApp->getCamera().getMode();
if (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_FIRST_PERSON) {
@ -2196,7 +2199,7 @@ void MyAvatar::computeMyLookAtTarget(const AvatarHash& hash) {
foreach (const AvatarSharedPointer& avatarData, hash) {
std::shared_ptr<Avatar> avatar = std::static_pointer_cast<Avatar>(avatarData);
if (!avatar->isMyAvatar() && avatar->isInitialized()) {
glm::vec3 otherForward = avatar->getHead()->getForwardDirection();
glm::vec3 otherForward = avatar->getHeadJointFrontVector();
glm::vec3 otherPosition = avatar->getHead()->getEyePosition();
const float TIME_WITHOUT_TALKING_THRESHOLD = 1.0f;
bool otherIsTalking = avatar->getHead()->getTimeWithoutTalking() <= TIME_WITHOUT_TALKING_THRESHOLD;
@ -2284,7 +2287,9 @@ void MyAvatar::updateLookAtTargetAvatar() {
AvatarHash hash = DependencyManager::get<AvatarManager>()->getHashCopy();
// determine what the best look at target for my avatar should be.
computeMyLookAtTarget(hash);
if (!_scriptControlsEyesLookAt) {
computeMyLookAtTarget(hash);
}
// snap look at position for avatars that are looking at me.
snapOtherAvatarLookAtTargetsToMe(hash);
@ -6619,7 +6624,7 @@ bool MyAvatar::getIsJointOverridden(int jointIndex) const {
return _skeletonModel->getIsJointOverridden(jointIndex);
}
void MyAvatar::updateLookAtPosition(FaceTracker* faceTracker, Camera& myCamera) {
void MyAvatar::updateEyesLookAtPosition(FaceTracker* faceTracker, Camera& myCamera, float deltaTime) {
updateLookAtTargetAvatar();
@ -6649,6 +6654,13 @@ void MyAvatar::updateLookAtPosition(FaceTracker* faceTracker, Camera& myCamera)
} else {
lookAtSpot = myHead->getEyePosition() + glm::normalize(leftVec) * 1000.0f;
}
} else if (_scriptControlsEyesLookAt) {
if (_scriptEyesControlTimer < MAX_LOOK_AT_TIME_SCRIPT_CONTROL) {
_scriptEyesControlTimer += deltaTime;
lookAtSpot = _eyesLookAtTarget.get();
} else {
_scriptControlsEyesLookAt = false;
}
} else {
controller::Pose leftEyePose = getControllerPoseInAvatarFrame(controller::Action::LEFT_EYE);
controller::Pose rightEyePose = getControllerPoseInAvatarFrame(controller::Action::RIGHT_EYE);
@ -6711,8 +6723,9 @@ void MyAvatar::updateLookAtPosition(FaceTracker* faceTracker, Camera& myCamera)
if (headPose.isValid()) {
lookAtSpot = transformPoint(headPose.getMatrix(), glm::vec3(0.0f, 0.0f, TREE_SCALE));
} else {
lookAtSpot = myHead->getEyePosition() +
(getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE));
lookAtSpot = _shouldTurnToFaceCamera ?
myHead->getLookAtPosition() :
myHead->getEyePosition() + getHeadJointFrontVector() * (float)TREE_SCALE;
}
}
@ -6732,7 +6745,7 @@ void MyAvatar::updateLookAtPosition(FaceTracker* faceTracker, Camera& myCamera)
}
}
}
_eyesLookAtTarget.set(lookAtSpot);
getHead()->setLookAtPosition(lookAtSpot);
}
@ -6815,6 +6828,17 @@ void MyAvatar::setHeadLookAt(const glm::vec3& lookAtTarget) {
_lookAtScriptTarget = lookAtTarget;
}
void MyAvatar::setEyesLookAt(const glm::vec3& lookAtTarget) {
if (QThread::currentThread() != thread()) {
BLOCKING_INVOKE_METHOD(this, "setEyesLookAt",
Q_ARG(const glm::vec3&, lookAtTarget));
return;
}
_eyesLookAtTarget.set(lookAtTarget);
_scriptEyesControlTimer = 0.0f;
_scriptControlsEyesLookAt = true;
}
glm::vec3 MyAvatar::getLookAtPivotPoint() {
glm::vec3 avatarUp = getWorldOrientation() * Vectors::UP;
glm::vec3 yAxisEyePosition = getWorldPosition() + avatarUp * glm::dot(avatarUp, _skeletonModel->getDefaultEyeModelPosition());
@ -6907,4 +6931,3 @@ void MyAvatar::resetPointAt() {
POINT_BLEND_LINEAR_ALPHA_NAME, POINT_ALPHA_BLENDING);
}
}

View file

@ -1765,10 +1765,26 @@ public:
/**jsdoc
* Returns the current head look at target point in world coordinates.
* @function MyAvatar.getHeadLookAt
* @returns {Vec3} Default position between your avatar's eyes in world coordinates.
* @returns {Vec3} The head's look at target in world coordinates.
*/
Q_INVOKABLE glm::vec3 getHeadLookAt() { return _lookAtCameraTarget; }
/**jsdoc
* Force the avatar's eyes to look to the specified location.
* Once this method is called, API calls will have full control of the eyes for a limited time.
* If this method is not called for two seconds, the engine will regain control of the eyes.
* @function MyAvatar.setEyesLookAt
* @param {Vec3} lookAtTarget - The target point in world coordinates.
*/
Q_INVOKABLE void setEyesLookAt(const glm::vec3& lookAtTarget);
/**jsdoc
* Returns the current eyes look at target point in world coordinates.
* @function MyAvatar.getEyesLookAt
* @returns {Vec3} The eyes's look at target in world coordinates.
*/
Q_INVOKABLE glm::vec3 getEyesLookAt() { return _eyesLookAtTarget.get(); }
/**jsdoc
* Aims the pointing directional blending towards the provided target point.
* The "point" reaction should be triggered before using this method.
@ -1906,7 +1922,7 @@ public:
bool getFlowActive() const;
bool getNetworkGraphActive() const;
void updateLookAtPosition(FaceTracker* faceTracker, Camera& myCamera);
void updateEyesLookAtPosition(FaceTracker* faceTracker, Camera& myCamera, float deltaTime);
// sets the reaction enabled and triggered parameters of the passed in params
// also clears internal reaction triggers
@ -2666,6 +2682,9 @@ private:
eyeContactTarget _eyeContactTarget;
float _eyeContactTargetTimer { 0.0f };
ThreadSafeValueCache<glm::vec3> _eyesLookAtTarget { glm::vec3() };
bool _scriptControlsEyesLookAt{ false };
float _scriptEyesControlTimer{ 0.0f };
glm::vec3 _trackedHeadPosition;

View file

@ -96,10 +96,16 @@ void Head::simulate(float deltaTime) {
// no blinking when brows are raised; blink less with increasing loudness
const float BASE_BLINK_RATE = 15.0f / 60.0f;
const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f;
if (forceBlink || (_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(fabs(_averageLoudness - _longTermAverageLoudness)) *
if (_forceBlinkToRetarget || forceBlink ||
(_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(fabs(_averageLoudness - _longTermAverageLoudness)) *
ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) {
float randSpeedVariability = randFloat();
float eyeBlinkVelocity = BLINK_SPEED + randSpeedVariability * BLINK_SPEED_VARIABILITY;
if (_forceBlinkToRetarget) {
// Slow down by half the blink if reseting eye target
eyeBlinkVelocity = 0.5f * eyeBlinkVelocity;
_forceBlinkToRetarget = false;
}
_leftEyeBlinkVelocity = eyeBlinkVelocity;
_rightEyeBlinkVelocity = eyeBlinkVelocity;
if (randFloat() < 0.5f) {
@ -114,13 +120,12 @@ void Head::simulate(float deltaTime) {
if (_leftEyeBlink == FULLY_CLOSED) {
_leftEyeBlinkVelocity = -BLINK_SPEED;
updateEyeLookAt();
} else if (_leftEyeBlink == FULLY_OPEN) {
_leftEyeBlinkVelocity = 0.0f;
}
if (_rightEyeBlink == FULLY_CLOSED) {
_rightEyeBlinkVelocity = -BLINK_SPEED;
} else if (_rightEyeBlink == FULLY_OPEN) {
_rightEyeBlinkVelocity = 0.0f;
}
@ -350,3 +355,24 @@ float Head::getFinalPitch() const {
float Head::getFinalRoll() const {
return glm::clamp(_baseRoll + _deltaRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL);
}
void Head::setLookAtPosition(const glm::vec3& lookAtPosition) {
if (_isEyeLookAtUpdated && _requestLookAtPosition != lookAtPosition) {
_lookAtPositionChanged = usecTimestampNow();
glm::vec3 oldAvatarLookAtVector = _requestLookAtPosition - _owningAvatar->getWorldPosition();
glm::vec3 newAvatarLookAtVector = lookAtPosition - _owningAvatar->getWorldPosition();
const float MIN_BLINK_ANGLE = 0.35f; // 20 degrees
_forceBlinkToRetarget = angleBetween(oldAvatarLookAtVector, newAvatarLookAtVector) > MIN_BLINK_ANGLE;
if (_forceBlinkToRetarget) {
_isEyeLookAtUpdated = false;
} else {
_lookAtPosition = lookAtPosition;
}
}
_requestLookAtPosition = lookAtPosition;
}
void Head::updateEyeLookAt() {
_lookAtPosition = _requestLookAtPosition;
_isEyeLookAtUpdated = true;
}

View file

@ -79,6 +79,9 @@ public:
float getTimeWithoutTalking() const { return _timeWithoutTalking; }
virtual void setLookAtPosition(const glm::vec3& lookAtPosition) override;
void updateEyeLookAt();
protected:
// disallow copies of the Head, copy of owning Avatar is disallowed too
Head(const Head&);
@ -123,6 +126,10 @@ protected:
int _leftEyeLookAtID;
int _rightEyeLookAtID;
glm::vec3 _requestLookAtPosition;
bool _forceBlinkToRetarget { false };
bool _isEyeLookAtUpdated { false };
// private methods
void calculateMouthShapes(float timeRatio);
void applyEyelidOffset(glm::quat headOrientation);

View file

@ -3214,3 +3214,12 @@ void AvatarData::clearAvatarGrabData(const QUuid& grabID) {
}
});
}
glm::vec3 AvatarData::getHeadJointFrontVector() const {
int headJointIndex = getJointIndex("Head");
glm::quat headJointRotation = Quaternions::Y_180 * getAbsoluteJointRotationInObjectFrame(headJointIndex);// getAbsoluteJointRotationInRigFrame(headJointIndex, headJointRotation);
headJointRotation = getWorldOrientation() * headJointRotation;
float headYaw = safeEulerAngles(headJointRotation).y;
glm::quat headYawRotation = glm::angleAxis(headYaw, Vectors::UP);
return headYawRotation * IDENTITY_FORWARD;
}

View file

@ -1484,6 +1484,7 @@ public:
std::vector<AvatarSkeletonTrait::UnpackedJointData> getSkeletonData() const;
void sendSkeletonData() const;
QVector<JointData> getJointData() const;
glm::vec3 getHeadJointFrontVector() const;
signals:

View file

@ -64,7 +64,7 @@ public:
void setBlendshapeCoefficients(const QVector<float>& blendshapeCoefficients) { _blendshapeCoefficients = blendshapeCoefficients; }
const glm::vec3& getLookAtPosition() const { return _lookAtPosition; }
void setLookAtPosition(const glm::vec3& lookAtPosition) {
virtual void setLookAtPosition(const glm::vec3& lookAtPosition) {
if (_lookAtPosition != lookAtPosition) {
_lookAtPositionChanged = usecTimestampNow();
}