From 53f47c132f61715221096c0aa234e9e6dd375ded Mon Sep 17 00:00:00 2001 From: trent Date: Thu, 27 Apr 2017 16:12:25 -0400 Subject: [PATCH] Avatar snap-turn rotation smoothing (sent out from the local client outward). --- interface/src/avatar/MyAvatar.cpp | 15 ++++++++++++++- libraries/avatars/src/AvatarData.cpp | 17 ++++++++++++++++- libraries/avatars/src/AvatarData.h | 21 +++++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f4f078c9e5..6300df1634 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -375,6 +375,10 @@ void MyAvatar::update(float deltaTime) { float tau = deltaTime / HMD_FACING_TIMESCALE; _hmdSensorFacingMovingAverage = lerp(_hmdSensorFacingMovingAverage, _hmdSensorFacing, tau); + if (_smoothOrientationTimer < _smoothOrientationTime) { + _smoothOrientationTimer = min(_smoothOrientationTimer + deltaTime, _smoothOrientationTime); + } + #ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE glm::vec3 p = transformPoint(getSensorToWorldMatrix(), _hmdSensorPosition + glm::vec3(_hmdSensorFacingMovingAverage.x, 0.0f, _hmdSensorFacingMovingAverage.y)); DebugDraw::getInstance().addMarker("facing-avg", getOrientation(), p, glm::vec4(1.0f)); @@ -1742,8 +1746,10 @@ void MyAvatar::updateOrientation(float deltaTime) { // Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll // get an instantaneous 15 degree turn. If you keep holding the key down you'll get another // snap turn every half second. + bool snapTurn = false; if (getDriveKey(STEP_YAW) != 0.0f) { totalBodyYaw += getDriveKey(STEP_YAW); + snapTurn = true; } // use head/HMD orientation to turn while flying @@ -1776,10 +1782,17 @@ void MyAvatar::updateOrientation(float deltaTime) { totalBodyYaw += (speedFactor * deltaAngle * (180.0f / PI)); } - // update body orientation by movement inputs + glm::quat initialOrientation = getOrientation(); setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f)))); + if (snapTurn) { + // Whether or not there is an existing smoothing going on, just reset the smoothing timer and set the starting position as the avatar's current position, then smooth to the new position. + _smoothOrientationInitial = initialOrientation; + _smoothOrientationTarget = getOrientation(); + _smoothOrientationTimer = 0.0f; + } + getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime); if (qApp->isHMDMode()) { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 20894104ff..a38c269831 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -67,7 +67,11 @@ AvatarData::AvatarData() : _displayNameAlpha(1.0f), _errorLogExpiry(0), _owningAvatarMixer(), - _targetVelocity(0.0f) + _targetVelocity(0.0f), + _smoothOrientationTime(SMOOTH_TIME_ORIENTATION), + _smoothOrientationTimer(std::numeric_limits::max()), + _smoothOrientationInitial(), + _smoothOrientationTarget() { setBodyPitch(0.0f); setBodyYaw(-90.0f); @@ -1460,6 +1464,17 @@ void AvatarData::parseAvatarIdentityPacket(const QByteArray& data, Identity& ide packetStream >> identityOut.uuid >> identityOut.skeletonModelURL >> identityOut.attachmentData >> identityOut.displayName >> identityOut.sessionDisplayName >> identityOut.avatarEntityData; } +glm::quat AvatarData::getLocalOrientation() const { + if (!isMyAvatar() || (_smoothOrientationTimer > _smoothOrientationTime)) { + return (SpatiallyNestable::getLocalOrientation()); + } + + // Smooth the remote avatar movement. + float t = _smoothOrientationTimer / _smoothOrientationTime; + float l = easeInOutQuad(glm::clamp(t, 0.0f, 1.0f)); + return (slerp(_smoothOrientationInitial, _smoothOrientationTarget, l)); +} + static const QUrl emptyURL(""); QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const { // We don't put file urls on the wire, but instead convert to empty. diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 8319eb5249..b68de2397b 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -603,6 +603,9 @@ public: bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_UPDATE_TIMEOUT; } + // Overload the local orientation function for this. + virtual glm::quat getLocalOrientation() const override; + static const float OUT_OF_VIEW_PENALTY; static void sortAvatars( @@ -619,7 +622,16 @@ public: static float _avatarSortCoefficientCenter; static float _avatarSortCoefficientAge; + // Basic ease-in-ease-out function for smoothing values. + static inline float easeInOutQuad(float lerpValue) { + assert(!((lerpValue < 0.0f) || (lerpValue > 1.0f))); + if (lerpValue < 0.5f) { + return (2.0f * lerpValue * lerpValue); + } + + return (lerpValue*(4.0f - 2.0f * lerpValue) - 1.0f); + } signals: void displayNameChanged(); @@ -778,6 +790,15 @@ protected: quint64 _audioLoudnessChanged { 0 }; float _audioAverageLoudness { 0.0f }; + // Smoothing. + const float SMOOTH_TIME_ORIENTATION = 0.15f; + + // Smoothing data for blending from one position/orientation to another on remote agents. + float _smoothOrientationTime; + float _smoothOrientationTimer; + glm::quat _smoothOrientationInitial; + glm::quat _smoothOrientationTarget; + private: friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); static QUrl _defaultFullAvatarModelUrl;