diff --git a/interface/resources/avatar/avatar-animation.json b/interface/resources/avatar/avatar-animation.json index 1412b45968..914fe8ba69 100644 --- a/interface/resources/avatar/avatar-animation.json +++ b/interface/resources/avatar/avatar-animation.json @@ -86,7 +86,9 @@ "typeVar": "rightFootType", "weightVar": "rightFootWeight", "weight": 1.0, - "flexCoefficients": [1, 0.45, 0.45] + "flexCoefficients": [1, 0.45, 0.45], + "poleJointNameVar": "rightFootPoleJointName", + "poleVectorVar": "rightFootPoleVector" }, { "jointName": "LeftFoot", @@ -95,7 +97,9 @@ "typeVar": "leftFootType", "weightVar": "leftFootWeight", "weight": 1.0, - "flexCoefficients": [1, 0.45, 0.45] + "flexCoefficients": [1, 0.45, 0.45], + "poleJointNameVar": "leftFootPoleJointName", + "poleVectorVar": "leftFootPoleVector" }, { "jointName": "Spine2", diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 0ff236d1b1..57fa6fd1e3 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -47,12 +47,15 @@ float easeOutExpo(float t) { } AnimInverseKinematics::IKTargetVar::IKTargetVar(const QString& jointNameIn, const QString& positionVarIn, const QString& rotationVarIn, - const QString& typeVarIn, const QString& weightVarIn, float weightIn, const std::vector& flexCoefficientsIn) : + const QString& typeVarIn, const QString& weightVarIn, float weightIn, const std::vector& flexCoefficientsIn, + const QString& poleJointNameVarIn, const QString& poleVectorVarIn) : jointName(jointNameIn), positionVar(positionVarIn), rotationVar(rotationVarIn), typeVar(typeVarIn), weightVar(weightVarIn), + poleJointNameVar(poleJointNameVarIn), + poleVectorVar(poleVectorVarIn), weight(weightIn), numFlexCoefficients(flexCoefficientsIn.size()), jointIndex(-1) @@ -69,6 +72,8 @@ AnimInverseKinematics::IKTargetVar::IKTargetVar(const IKTargetVar& orig) : rotationVar(orig.rotationVar), typeVar(orig.typeVar), weightVar(orig.weightVar), + poleJointNameVar(orig.poleJointNameVar), + poleVectorVar(orig.poleVectorVar), weight(orig.weight), numFlexCoefficients(orig.numFlexCoefficients), jointIndex(orig.jointIndex) @@ -122,8 +127,9 @@ void AnimInverseKinematics::computeAbsolutePoses(AnimPoseVec& absolutePoses) con } void AnimInverseKinematics::setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar, - const QString& typeVar, const QString& weightVar, float weight, const std::vector& flexCoefficients) { - IKTargetVar targetVar(jointName, positionVar, rotationVar, typeVar, weightVar, weight, flexCoefficients); + const QString& typeVar, const QString& weightVar, float weight, const std::vector& flexCoefficients, + const QString& poleJointNameVar, const QString& poleVectorVar) { + IKTargetVar targetVar(jointName, positionVar, rotationVar, typeVar, weightVar, weight, flexCoefficients, poleJointNameVar, poleVectorVar); // if there are dups, last one wins. bool found = false; @@ -170,63 +176,14 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: target.setIndex(targetVar.jointIndex); target.setWeight(weight); target.setFlexCoefficients(targetVar.numFlexCoefficients, targetVar.flexCoefficients); - target.setPoleIndex(-1); - /* - // AJT: HACK REMOVE manually set pole vector. - if (targetVar.jointName == "RightHand") { - int armJointIndex = _skeleton->nameToJointIndex("RightArm"); - glm::vec3 armPosition = _skeleton->getAbsolutePose(armJointIndex, _relativePoses).trans(); - glm::vec3 d = glm::normalize(translation - armPosition); + // AJT: TODO: cache the pole joint index. + QString poleJointName = animVars.lookup(targetVar.poleJointNameVar, QString("")); + int poleJointIndex = _skeleton->nameToJointIndex(poleJointName); + target.setPoleIndex(poleJointIndex); - // project hand y-axis onto d. - glm::vec3 handY = rotation * Vectors::UNIT_Y; - glm::vec3 handYProj = handY - glm::dot(handY, d) * d; - - // project negative pole vector on to d. - glm::vec3 p = glm::normalize(glm::vec3(-1, -3, -3)); - glm::vec3 pProj = p - glm::dot(p, d) * d; - - // compute a rotation around d that will bring the pProj to to yProj - float theta = acosf(glm::dot(glm::normalize(-pProj), glm::normalize(handYProj))); - glm::vec3 axis = glm::normalize(glm::cross(-pProj, handYProj)); - - if (glm::dot(axis, d) < 0.0f) { - // as eProjLen and pProjLen become orthognal to d, reduce the amount of rotation. - float magnitude = easeOutExpo(glm::min(glm::length(handYProj), glm::length(pProj))); - glm::quat poleAdjustRot = angleAxis(magnitude * (theta / 4.0f), axis); - target.setPoleVector(poleAdjustRot * p); - } else { - target.setPoleVector(p); - } - target.setPoleIndex(_skeleton->nameToJointIndex(targetVar.jointName)); - } - if (targetVar.jointName == "LeftHand") { - target.setPoleVector(glm::normalize(glm::vec3(1, -3, -3))); - target.setPoleIndex(_skeleton->nameToJointIndex(targetVar.jointName)); - } - */ - - if (targetVar.jointName == "LeftFoot" || targetVar.jointName == "RightFoot") { - - // compute the forward direction of the foot. - AnimPose defaultPose = _skeleton->getAbsoluteDefaultPose(targetVar.jointIndex); - glm::vec3 localForward = glm::inverse(defaultPose.rot()) * Vectors::UNIT_Z; - glm::vec3 footForward = rotation * localForward; - - // compute the forward direction of the hips. - glm::quat hipsRotation = _skeleton->getAbsolutePose(_hipsIndex, _relativePoses).rot(); - glm::vec3 hipsForward = hipsRotation * Vectors::UNIT_Z; - - // blend between the hips and the foot. - const float FOOT_TO_HIPS_BLEND_FACTOR = 0.5f; - glm::vec3 poleVector = glm::normalize(lerp(footForward, hipsForward, FOOT_TO_HIPS_BLEND_FACTOR)); - - // TODO: smooth toward desired pole vector from previous pole vector... to reduce jitter - - target.setPoleVector(poleVector); - target.setPoleIndex(_skeleton->nameToJointIndex(targetVar.jointName)); - } + glm::vec3 poleVector = animVars.lookupRigToGeometryVector(targetVar.poleVectorVar, Vectors::UNIT_Z); + target.setPoleVector(glm::normalize(poleVector)); targets.push_back(target); diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index 2ed96ed6ed..946c23883e 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -42,7 +42,8 @@ public: void computeAbsolutePoses(AnimPoseVec& absolutePoses) const; void setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar, - const QString& typeVar, const QString& weightVar, float weight, const std::vector& flexCoefficients); + const QString& typeVar, const QString& weightVar, float weight, const std::vector& flexCoefficients, + const QString& poleJointNameVar, const QString& poleVectorVar); virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimNode::Triggers& triggersOut) override; virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override; @@ -108,7 +109,8 @@ protected: enum FlexCoefficients { MAX_FLEX_COEFFICIENTS = 10 }; struct IKTargetVar { IKTargetVar(const QString& jointNameIn, const QString& positionVarIn, const QString& rotationVarIn, - const QString& typeVarIn, const QString& weightVarIn, float weightIn, const std::vector& flexCoefficientsIn); + const QString& typeVarIn, const QString& weightVarIn, float weightIn, const std::vector& flexCoefficientsIn, + const QString& poleJointNameVar, const QString& poleVectorVar); IKTargetVar(const IKTargetVar& orig); QString jointName; @@ -116,6 +118,8 @@ protected: QString rotationVar; QString typeVar; QString weightVar; + QString poleJointNameVar; + QString poleVectorVar; float weight; float flexCoefficients[MAX_FLEX_COEFFICIENTS]; size_t numFlexCoefficients; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 44ed8c6053..c4a149a35b 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -479,6 +479,8 @@ AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QS READ_OPTIONAL_STRING(typeVar, targetObj); READ_OPTIONAL_STRING(weightVar, targetObj); READ_OPTIONAL_FLOAT(weight, targetObj, 1.0f); + READ_OPTIONAL_STRING(poleJointNameVar, targetObj); + READ_OPTIONAL_STRING(poleVectorVar, targetObj); auto flexCoefficientsValue = targetObj.value("flexCoefficients"); if (!flexCoefficientsValue.isArray()) { @@ -491,7 +493,7 @@ AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QS flexCoefficients.push_back((float)value.toDouble()); } - node->setTargetVars(jointName, positionVar, rotationVar, typeVar, weightVar, weight, flexCoefficients); + node->setTargetVars(jointName, positionVar, rotationVar, typeVar, weightVar, weight, flexCoefficients, poleJointNameVar, poleVectorVar); }; READ_OPTIONAL_STRING(solutionSource, jsonObj); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 20a2aab2b6..2966e882d2 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -479,12 +479,6 @@ bool Rig::getAbsoluteJointPoseInRigFrame(int jointIndex, AnimPose& returnPose) c } } -bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const { - // AJT: TODO: used by attachments - ASSERT(false); - return false; -} - void Rig::calcAnimAlpha(float speed, const std::vector& referenceSpeeds, float* alphaOut) const { ASSERT(referenceSpeeds.size() > 0); @@ -950,6 +944,7 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons // evaluate the animation AnimNode::Triggers triggersOut; + _internalPoseSet._relativePoses = _animNode->evaluate(_animVars, context, deltaTime, triggersOut); if ((int)_internalPoseSet._relativePoses.size() != _animSkeleton->getNumJoints()) { // animations haven't fully loaded yet. @@ -1145,6 +1140,21 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm } } +glm::vec3 Rig::calculateKneePoleVector(int footJointIndex, const glm::quat& footTargetOrientation, int hipsIndex) const { + + AnimPose defaultPose = _animSkeleton->getAbsoluteDefaultPose(footJointIndex); + glm::vec3 localForward = glm::inverse(defaultPose.rot()) * Vectors::UNIT_Z; + glm::vec3 footForward = footTargetOrientation * localForward; + + // compute the forward direction of the hips. + glm::quat hipsRotation = _externalPoseSet._absolutePoses[hipsIndex].rot(); + glm::vec3 hipsForward = hipsRotation * Vectors::UNIT_Z; + + // blend between the hips and the foot. + const float FOOT_TO_HIPS_BLEND_FACTOR = 0.5f; + return glm::normalize(lerp(footForward, hipsForward, FOOT_TO_HIPS_BLEND_FACTOR)); +} + void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, float dt) { if (_animSkeleton && _animNode) { const float HAND_RADIUS = 0.05f; @@ -1255,16 +1265,46 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f _animVars.set("leftFootPosition", params.leftFootPosition); _animVars.set("leftFootRotation", params.leftFootOrientation); _animVars.set("leftFootType", (int)IKTarget::Type::RotationAndPosition); + + int footJointIndex = _animSkeleton->nameToJointIndex("LeftFoot"); + glm::vec3 poleVector = calculateKneePoleVector(footJointIndex, params.leftFootOrientation, hipsIndex); + + // smooth toward desired pole vector from previous pole vector... to reduce jitter + if (!_prevLeftFootPoleVectorValid) { + _prevLeftFootPoleVectorValid = true; + _prevLeftFootPoleVector = poleVector; + } + const float POLE_VECTOR_BLEND_FACTOR = 0.9f; + _prevLeftFootPoleVector = lerp(poleVector, _prevLeftFootPoleVector, POLE_VECTOR_BLEND_FACTOR); + + _animVars.set("leftFootPoleVector", _prevLeftFootPoleVector); + _animVars.set("leftFootPoleJointName", QString("LeftFoot")); + } else { _animVars.unset("leftFootPosition"); _animVars.unset("leftFootRotation"); _animVars.set("leftFootType", (int)IKTarget::Type::RotationAndPosition); + _prevLeftFootPoleVectorValid = false; } if (params.isRightFootEnabled) { _animVars.set("rightFootPosition", params.rightFootPosition); _animVars.set("rightFootRotation", params.rightFootOrientation); _animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition); + + int footJointIndex = _animSkeleton->nameToJointIndex("RightFoot"); + glm::vec3 poleVector = calculateKneePoleVector(footJointIndex, params.rightFootOrientation, hipsIndex); + + // smooth toward desired pole vector from previous pole vector... to reduce jitter + if (!_prevRightFootPoleVectorValid) { + _prevRightFootPoleVectorValid = true; + _prevRightFootPoleVector = poleVector; + } + const float POLE_VECTOR_BLEND_FACTOR = 0.9f; + _prevRightFootPoleVector = lerp(poleVector, _prevRightFootPoleVector, POLE_VECTOR_BLEND_FACTOR); + + _animVars.set("rightFootPoleVector", _prevRightFootPoleVector); + _animVars.set("rightFootPoleJointName", QString("RightFoot")); } else { _animVars.unset("rightFootPosition"); _animVars.unset("rightFootRotation"); @@ -1455,22 +1495,18 @@ void Rig::computeAvatarBoundingCapsule( AnimInverseKinematics ikNode("boundingShape"); ikNode.setSkeleton(_animSkeleton); - ikNode.setTargetVars("LeftHand", - "leftHandPosition", - "leftHandRotation", - "leftHandType", "leftHandWeight", 1.0f, {}); - ikNode.setTargetVars("RightHand", - "rightHandPosition", - "rightHandRotation", - "rightHandType", "rightHandWeight", 1.0f, {}); - ikNode.setTargetVars("LeftFoot", - "leftFootPosition", - "leftFootRotation", - "leftFootType", "leftFootWeight", 1.0f, {}); - ikNode.setTargetVars("RightFoot", - "rightFootPosition", - "rightFootRotation", - "rightFootType", "rightFootWeight", 1.0f, {}); + ikNode.setTargetVars("LeftHand", "leftHandPosition", "leftHandRotation", + "leftHandType", "leftHandWeight", 1.0f, {}, + "leftHandPoleJointName", "leftHandPoleVector"); + ikNode.setTargetVars("RightHand", "rightHandPosition", "rightHandRotation", + "rightHandType", "rightHandWeight", 1.0f, {}, + "rightHandPoleJointName", "rightHandPoleVector"); + ikNode.setTargetVars("LeftFoot", "leftFootPosition", "leftFootRotation", + "leftFootType", "leftFootWeight", 1.0f, {}, + "leftFootPoleJointName", "leftFootPoleVector"); + ikNode.setTargetVars("RightFoot", "rightFootPosition", "rightFootRotation", + "rightFootType", "rightFootWeight", 1.0f, {}, + "rightFootPoleJointName", "rightFootPoleVector"); AnimPose geometryToRig = _modelOffset * _geometryOffset; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 994bd4b074..4fc5f76146 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -153,9 +153,6 @@ public: bool getAbsoluteJointTranslationInRigFrame(int jointIndex, glm::vec3& translation) const; bool getAbsoluteJointPoseInRigFrame(int jointIndex, AnimPose& returnPose) const; - // legacy - bool getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const; - // rig space glm::mat4 getJointTransform(int jointIndex) const; @@ -252,6 +249,8 @@ protected: void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::vec3& lookAt, const glm::vec3& saccade); void calcAnimAlpha(float speed, const std::vector& referenceSpeeds, float* alphaOut) const; + glm::vec3 Rig::calculateKneePoleVector(int footJointIndex, const glm::quat& footTargetOrientation, int hipsIndex) const; + AnimPose _modelOffset; // model to rig space AnimPose _geometryOffset; // geometry to model space (includes unit offset & fst offsets) AnimPose _invGeometryOffset; @@ -347,7 +346,6 @@ protected: bool _enableDebugDrawIKConstraints { false }; bool _enableDebugDrawIKChains { false }; -private: QMap _stateHandlers; int _nextStateHandlerId { 0 }; QMutex _stateMutex; @@ -358,6 +356,12 @@ private: float _rightHandRelaxDuration { 0.0f }; AnimPose _lastLeftHandControlledPose; AnimPose _lastRightHandControlledPose; + + glm::vec3 _prevRightFootPoleVector = { Vectors::UNIT_Z }; + bool _prevRightFootPoleVectorValid = { false }; + + glm::vec3 _prevLeftFootPoleVector = { Vectors::UNIT_Z }; + bool _prevLeftFootPoleVectorValid = { false }; }; #endif /* defined(__hifi__Rig__) */ diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 447d0e37bd..8953ea94f0 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -866,10 +866,6 @@ bool Model::getRelativeDefaultJointTranslation(int jointIndex, glm::vec3& transl return _rig.getRelativeDefaultJointTranslation(jointIndex, translationOut); } -bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const { - return _rig.getJointCombinedRotation(jointIndex, rotation, _rotation); -} - QStringList Model::getJointNames() const { if (QThread::currentThread() != thread()) { QStringList result; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 53d446d306..a0da8a7a8c 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -176,7 +176,6 @@ public: int getJointStateCount() const { return (int)_rig.getJointStateCount(); } bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const; bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const; - bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const; /// \param jointIndex index of joint in model structure /// \param rotation[out] rotation of joint in model-frame