mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-07 21:42:31 +02:00
Merge pull request #16317 from luiscuenca/setAvatarLookAtAPI
DEV-2285: Improve eyes look at
This commit is contained in:
commit
edb181bd16
9 changed files with 103 additions and 18 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(); }
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -1484,6 +1484,7 @@ public:
|
|||
std::vector<AvatarSkeletonTrait::UnpackedJointData> getSkeletonData() const;
|
||||
void sendSkeletonData() const;
|
||||
QVector<JointData> getJointData() const;
|
||||
glm::vec3 getHeadJointFrontVector() const;
|
||||
|
||||
signals:
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue