From 02d57699916c60fe3aa816b192c170bd2cb1592e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 9 Apr 2019 12:57:03 -0700 Subject: [PATCH] Better head vs camera checks for avatar head cauterization * cameraInsideHead() check now uses detailed avatar collision when possible. * head is now more constantly hidden in first person camera mode * getEyeModelPositions() uses a better estimate when avatar eye joints are missing. * moved findPointKDopDisplacement from Rig.cpp into AnimUtil.cpp * added isPlayingOverrideAnimation() method to Rig class --- interface/src/avatar/MyAvatar.cpp | 29 +++++++- libraries/animation/src/AnimUtil.cpp | 69 +++++++++++++++++++ libraries/animation/src/AnimUtil.h | 6 ++ libraries/animation/src/Rig.cpp | 68 ------------------ libraries/animation/src/Rig.h | 5 +- .../src/avatars-renderer/SkeletonModel.cpp | 29 ++------ libraries/shared/src/AvatarConstants.h | 1 + 7 files changed, 112 insertions(+), 95 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 568b492b46..dcd768f2ae 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3180,17 +3180,40 @@ int MyAvatar::sendAvatarDataPacket(bool sendAll) { return bytesSent; } -const float RENDER_HEAD_CUTOFF_DISTANCE = 0.47f; - bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const { + if (!_skeletonModel) { + return false; + } + + // transform cameraPosition into rig coordinates + AnimPose rigToWorld = AnimPose(getWorldOrientation() * Quaternions::Y_180, getWorldPosition()); + AnimPose worldToRig = rigToWorld.inverse(); + glm::vec3 rigCameraPosition = worldToRig * cameraPosition; + + // use head k-dop shape to determine if camera is inside head. + const Rig& rig = _skeletonModel->getRig(); + int headJointIndex = rig.indexOfJoint("Head"); + if (headJointIndex >= 0) { + const HFMModel& hfmModel = _skeletonModel->getHFMModel(); + AnimPose headPose; + if (rig.getAbsoluteJointPoseInRigFrame(headJointIndex, headPose)) { + glm::vec3 displacement; + const HFMJointShapeInfo& headShapeInfo = hfmModel.joints[headJointIndex].shapeInfo; + return findPointKDopDisplacement(rigCameraPosition, headPose, headShapeInfo, displacement); + } + } + + // fall back to simple distance check. + const float RENDER_HEAD_CUTOFF_DISTANCE = 0.47f; return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getModelScale()); } bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { bool defaultMode = renderArgs->_renderMode == RenderArgs::DEFAULT_RENDER_MODE; bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON; + bool overrideAnim = _skeletonModel ? _skeletonModel->getRig().isPlayingOverrideAnimation() : false; bool insideHead = cameraInsideHead(renderArgs->getViewFrustum().getPosition()); - return !defaultMode || !firstPerson || !insideHead; + return !defaultMode || (!firstPerson && !insideHead) || (overrideAnim && !insideHead); } void MyAvatar::setHasScriptedBlendshapes(bool hasScriptedBlendshapes) { diff --git a/libraries/animation/src/AnimUtil.cpp b/libraries/animation/src/AnimUtil.cpp index c23e228556..5fca2b4f88 100644 --- a/libraries/animation/src/AnimUtil.cpp +++ b/libraries/animation/src/AnimUtil.cpp @@ -142,3 +142,72 @@ glm::quat computeBodyFacingFromHead(const glm::quat& headRot, const glm::vec3& u return glmExtractRotation(bodyMat); } + + +const float INV_SQRT_3 = 1.0f / sqrtf(3.0f); +const int DOP14_COUNT = 14; +const glm::vec3 DOP14_NORMALS[DOP14_COUNT] = { + Vectors::UNIT_X, + -Vectors::UNIT_X, + Vectors::UNIT_Y, + -Vectors::UNIT_Y, + Vectors::UNIT_Z, + -Vectors::UNIT_Z, + glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3), + -glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3), + glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3), + -glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3), + glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3), + -glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3), + glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3), + -glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3) +}; + +// returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose. +// if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward +// such that it lies on the surface of the kdop. +bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut) { + + // transform point into local space of jointShape. + glm::vec3 localPoint = shapePose.inverse().xformPoint(point); + + // Only works for 14-dop shape infos. + if (shapeInfo.dots.size() != DOP14_COUNT) { + return false; + } + + glm::vec3 minDisplacement(FLT_MAX); + float minDisplacementLen = FLT_MAX; + glm::vec3 p = localPoint - shapeInfo.avgPoint; + float pLen = glm::length(p); + if (pLen > 0.0f) { + int slabCount = 0; + for (int i = 0; i < DOP14_COUNT; i++) { + float dot = glm::dot(p, DOP14_NORMALS[i]); + if (dot > 0.0f && dot < shapeInfo.dots[i]) { + slabCount++; + float distToPlane = pLen * (shapeInfo.dots[i] / dot); + float displacementLen = distToPlane - pLen; + + // keep track of the smallest displacement + if (displacementLen < minDisplacementLen) { + minDisplacementLen = displacementLen; + minDisplacement = (p / pLen) * displacementLen; + } + } + } + if (slabCount == (DOP14_COUNT / 2) && minDisplacementLen != FLT_MAX) { + // we are within the k-dop so push the point along the minimum displacement found + displacementOut = shapePose.xformVectorFast(minDisplacement); + return true; + } else { + // point is outside of kdop + return false; + } + } else { + // point is directly on top of shapeInfo.avgPoint. + // push the point out along the x axis. + displacementOut = shapePose.xformVectorFast(shapeInfo.points[0]); + return true; + } +} diff --git a/libraries/animation/src/AnimUtil.h b/libraries/animation/src/AnimUtil.h index cf190e8dbf..c2925e31e8 100644 --- a/libraries/animation/src/AnimUtil.h +++ b/libraries/animation/src/AnimUtil.h @@ -128,4 +128,10 @@ protected: bool _snapshotValid { false }; }; + +// returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose. +// if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward +// such that it lies on the surface of the kdop. +bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut); + #endif diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 43e94d23e8..6e434eb68d 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1521,74 +1521,6 @@ void Rig::updateHead(bool headEnabled, bool hipsEnabled, const AnimPose& headPos } } -const float INV_SQRT_3 = 1.0f / sqrtf(3.0f); -const int DOP14_COUNT = 14; -const glm::vec3 DOP14_NORMALS[DOP14_COUNT] = { - Vectors::UNIT_X, - -Vectors::UNIT_X, - Vectors::UNIT_Y, - -Vectors::UNIT_Y, - Vectors::UNIT_Z, - -Vectors::UNIT_Z, - glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3), - -glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3), - glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3), - -glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3), - glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3), - -glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3), - glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3), - -glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3) -}; - -// returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose. -// if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward -// such that it lies on the surface of the kdop. -static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut) { - - // transform point into local space of jointShape. - glm::vec3 localPoint = shapePose.inverse().xformPoint(point); - - // Only works for 14-dop shape infos. - if (shapeInfo.dots.size() != DOP14_COUNT) { - return false; - } - - glm::vec3 minDisplacement(FLT_MAX); - float minDisplacementLen = FLT_MAX; - glm::vec3 p = localPoint - shapeInfo.avgPoint; - float pLen = glm::length(p); - if (pLen > 0.0f) { - int slabCount = 0; - for (int i = 0; i < DOP14_COUNT; i++) { - float dot = glm::dot(p, DOP14_NORMALS[i]); - if (dot > 0.0f && dot < shapeInfo.dots[i]) { - slabCount++; - float distToPlane = pLen * (shapeInfo.dots[i] / dot); - float displacementLen = distToPlane - pLen; - - // keep track of the smallest displacement - if (displacementLen < minDisplacementLen) { - minDisplacementLen = displacementLen; - minDisplacement = (p / pLen) * displacementLen; - } - } - } - if (slabCount == (DOP14_COUNT / 2) && minDisplacementLen != FLT_MAX) { - // we are within the k-dop so push the point along the minimum displacement found - displacementOut = shapePose.xformVectorFast(minDisplacement); - return true; - } else { - // point is outside of kdop - return false; - } - } else { - // point is directly on top of shapeInfo.avgPoint. - // push the point out along the x axis. - displacementOut = shapePose.xformVectorFast(shapeInfo.points[0]); - return true; - } -} - glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo, const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo) const { glm::vec3 position = handPosition; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index df13ff5c2b..1a511b2487 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -116,8 +116,9 @@ public: void destroyAnimGraph(); void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); + bool isPlayingOverrideAnimation() const { return _userAnimState.clipNodeEnum != UserAnimState::None; }; void restoreAnimation(); - + void overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); void triggerNetworkRole(const QString& role); void restoreNetworkAnimation(); @@ -333,7 +334,7 @@ protected: RigRole _state { RigRole::Idle }; RigRole _desiredState { RigRole::Idle }; float _desiredStateAge { 0.0f }; - + struct NetworkAnimState { enum ClipNodeEnum { None = 0, diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index fbcf36a8c9..6276b049a1 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -270,28 +270,13 @@ bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& getJointPosition(_rig.indexOfJoint("RightEye"), secondEyePosition)) { return true; } - // no eye joints; try to estimate based on head/neck joints - glm::vec3 neckPosition, headPosition; - if (getJointPosition(_rig.indexOfJoint("Neck"), neckPosition) && - getJointPosition(_rig.indexOfJoint("Head"), headPosition)) { - const float EYE_PROPORTION = 0.6f; - glm::vec3 baseEyePosition = glm::mix(neckPosition, headPosition, EYE_PROPORTION); - glm::quat headRotation; - getJointRotation(_rig.indexOfJoint("Head"), headRotation); - const float EYES_FORWARD = 0.25f; - const float EYE_SEPARATION = 0.1f; - float headHeight = glm::distance(neckPosition, headPosition); - firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight; - secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight; - return true; - } else if (getJointPosition(_rig.indexOfJoint("Head"), headPosition)) { - glm::vec3 baseEyePosition = headPosition; - glm::quat headRotation; - getJointRotation(_rig.indexOfJoint("Head"), headRotation); - const float EYES_FORWARD_HEAD_ONLY = 0.30f; - const float EYE_SEPARATION = 0.1f; - firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY); - secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY); + + glm::vec3 headPosition; + if (getJointPosition(_rig.indexOfJoint("Head"), headPosition)) { + float heightRatio = _rig.getUnscaledEyeHeight() / DEFAULT_AVATAR_EYE_HEIGHT; + glm::vec3 ipdOffset = glm::vec3(DEFAULT_AVATAR_IPD / 2.0f, 0.0f, 0.0f); + firstEyePosition = headPosition + heightRatio * (DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET + ipdOffset); + secondEyePosition = headPosition + heightRatio * (DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET - ipdOffset); return true; } return false; diff --git a/libraries/shared/src/AvatarConstants.h b/libraries/shared/src/AvatarConstants.h index 5166cb7a0b..9338249e99 100644 --- a/libraries/shared/src/AvatarConstants.h +++ b/libraries/shared/src/AvatarConstants.h @@ -42,6 +42,7 @@ const float DEFAULT_AVATAR_HIPS_MASS = 40.0f; const float DEFAULT_AVATAR_HEAD_MASS = 20.0f; const float DEFAULT_AVATAR_LEFTHAND_MASS = 2.0f; const float DEFAULT_AVATAR_RIGHTHAND_MASS = 2.0f; +const float DEFAULT_AVATAR_IPD = 0.064f; // Used when avatar is missing joints... (avatar space) const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 };