From 3cee3cbb5aa4b28f43cea90fb2e509e859f6127a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 25 Jan 2016 18:32:12 -0800 Subject: [PATCH] Avatar Developer Menu: added animation debug options This options are for for developers only and might help debug animation related issues. * Enable Inverse Kinematics: this can be toggled to disable IK for the avatar. * Enable Anim Pre and Post Rotations: this option can be used to use FBX pre-rotations from source avatar animations, instead of the current default, which is to use them from the source model. This only effects FBX files loaded by the animation system, it does not affect changing model orientations via JavaScript. --- interface/src/Menu.cpp | 4 ++ interface/src/Menu.h | 2 + interface/src/avatar/MyAvatar.cpp | 10 +++++ interface/src/avatar/MyAvatar.h | 2 + libraries/animation/src/AnimClip.cpp | 60 +++++++++++++++++----------- libraries/animation/src/AnimClip.h | 3 ++ libraries/animation/src/Rig.cpp | 10 +++++ libraries/animation/src/Rig.h | 4 ++ 8 files changed, 72 insertions(+), 23 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 9cbb031a61..f1203b9df1 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -479,6 +479,10 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, false, + avatar, SLOT(setUseAnimPreAndPostRotations(bool))); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true, + avatar, SLOT(setEnableInverseKinematics(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::KeyboardMotorControl, Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar, SLOT(updateMotionBehaviorFromMenu()), diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 408ab15d5e..7b9cca2c63 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -208,6 +208,7 @@ namespace MenuOption { const QString EchoServerAudio = "Echo Server Audio"; const QString Enable3DTVMode = "Enable 3DTV Mode"; const QString EnableCharacterController = "Enable avatar collisions"; + const QString EnableInverseKinematics = "Enable Inverse Kinematics"; const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation"; const QString ExpandMyAvatarTiming = "Expand /myAvatar"; const QString ExpandOtherAvatarTiming = "Expand /otherAvatar"; @@ -302,6 +303,7 @@ namespace MenuOption { const QString UploadAsset = "Upload File to Asset Server"; const QString UseAudioForMouth = "Use Audio for Mouth"; const QString UseCamera = "Use Camera"; + const QString UseAnimPreAndPostRotations = "Use Anim Pre and Post Rotations"; const QString VelocityFilter = "Velocity Filter"; const QString VisibleToEveryone = "Everyone"; const QString VisibleToFriends = "Friends"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e06dce2324..756dc6adea 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -647,6 +648,15 @@ void MyAvatar::setEnableMeshVisible(bool isEnabled) { _skeletonModel.setVisibleInScene(isEnabled, scene); } +void MyAvatar::setUseAnimPreAndPostRotations(bool isEnabled) { + AnimClip::usePreAndPostPoseFromAnim = isEnabled; + reset(true); +} + +void MyAvatar::setEnableInverseKinematics(bool isEnabled) { + _rig->setEnableInverseKinematics(isEnabled); +} + void MyAvatar::loadData() { Settings settings; settings.beginGroup("Avatar"); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 10ce962821..ed6c3cb883 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -260,6 +260,8 @@ public slots: void setEnableDebugDrawPosition(bool isEnabled); bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); } void setEnableMeshVisible(bool isEnabled); + void setUseAnimPreAndPostRotations(bool isEnabled); + void setEnableInverseKinematics(bool isEnabled); Q_INVOKABLE void setAnimGraphUrl(const QUrl& url); glm::vec3 getPositionForAudio(); diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 986bb0a30e..32da64808b 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -13,6 +13,8 @@ #include "AnimationLogging.h" #include "AnimUtil.h" +bool AnimClip::usePreAndPostPoseFromAnim = false; + AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag) : AnimNode(AnimNode::Type::Clip, id), _startFrame(startFrame), @@ -109,6 +111,8 @@ void AnimClip::copyFromNetworkAnim() { for (int frame = 0; frame < frameCount; frame++) { + const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame]; + // init all joints in animation to default pose // this will give us a resonable result for bones in the model skeleton but not in the animation. _anim[frame].reserve(skeletonJointCount); @@ -119,36 +123,46 @@ void AnimClip::copyFromNetworkAnim() { for (int animJoint = 0; animJoint < animJointCount; animJoint++) { int skeletonJoint = jointMap[animJoint]; + const glm::vec3& fbxAnimTrans = fbxAnimFrame.translations[animJoint]; + const glm::quat& fbxAnimRot = fbxAnimFrame.rotations[animJoint]; + // skip joints that are in the animation but not in the skeleton. if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) { - const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint]; -#ifdef USE_PRE_ROT_FROM_ANIM - // TODO: This is the correct way to apply the pre rotations from maya, however - // the current animation set has incorrect preRotations for the left wrist and thumb - // so it looks wrong if we enable this code. - glm::quat preRotation = animSkeleton.getPreRotation(animJoint); -#else - // TODO: This is the legacy approach, this does not work when animations and models do not - // have the same set of pre rotations. For example when mixing maya models with blender animations. - glm::quat preRotation = _skeleton->getRelativeBindPose(skeletonJoint).rot; -#endif - const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint); + AnimPose preRot, postRot; + if (usePreAndPostPoseFromAnim) { + preRot = animSkeleton.getPreRotationPose(animJoint); + postRot = animSkeleton.getPostRotationPose(animJoint); + } else { + // In order to support Blender, which does not have preRotation FBX support, we use the models defaultPose as the reference frame for the animations. + preRot = AnimPose(glm::vec3(1.0f), _skeleton->getRelativeBindPose(skeletonJoint).rot, glm::vec3()); + postRot = AnimPose::identity; + } - // used to adjust translation offsets, so large translation animatons on the reference skeleton + // cancel out scale + preRot.scale = glm::vec3(1.0f); + postRot.scale = glm::vec3(1.0f); + + AnimPose rot(glm::vec3(1.0f), fbxAnimRot, glm::vec3()); + + // adjust translation offsets, so large translation animatons on the reference skeleton // will be adjusted when played on a skeleton with short limbs. - float limbLengthScale = fabsf(glm::length(fbxZeroTrans)) <= 0.0001f ? 1.0f : (glm::length(relDefaultPose.trans) / glm::length(fbxZeroTrans)); + const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint]; + const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint); + float boneLengthScale = 1.0f; + const float EPSILON = 0.0001f; + if (fabsf(glm::length(fbxZeroTrans)) > EPSILON) { + boneLengthScale = glm::length(relDefaultPose.trans) / glm::length(fbxZeroTrans); + } - AnimPose& pose = _anim[frame][skeletonJoint]; - const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame]; + AnimPose trans; + if (usePreAndPostPoseFromAnim) { + trans = AnimPose(glm::vec3(1.0f), glm::quat(), boneLengthScale * (fbxAnimTrans + (fbxAnimTrans - fbxZeroTrans))); + } else { + trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans + boneLengthScale * (fbxAnimTrans - fbxZeroTrans)); + } - // rotation in fbxAnimationFrame is a delta from its preRotation. - pose.rot = preRotation * fbxAnimFrame.rotations[animJoint]; - - // translation in fbxAnimationFrame is not a delta. - // convert it into a delta by subtracting from the first frame. - const glm::vec3& fbxTrans = fbxAnimFrame.translations[animJoint]; - pose.trans = relDefaultPose.trans + limbLengthScale * (fbxTrans - fbxZeroTrans); + _anim[frame][skeletonJoint] = trans * preRot * rot * postRot; } } } diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 934f3f3ed8..91215d0d44 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -25,6 +25,9 @@ class AnimClip : public AnimNode { public: friend class AnimTests; + static bool usePreAndPostPoseFromAnim; + static bool useTransFromAnim; + AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag); virtual ~AnimClip() override; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index df92d7d912..4d72bfbaea 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -464,6 +464,10 @@ void Rig::computeEyesInRootFrame(const AnimPoseVec& poses) { } } +void Rig::setEnableInverseKinematics(bool enable) { + _enableInverseKinematics = enable; +} + AnimPose Rig::getAbsoluteDefaultPose(int index) const { if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) { return _absoluteDefaultPoses[index]; @@ -705,6 +709,12 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } t += deltaTime; + + if (_enableInverseKinematics) { + _animVars.set("ikOverlayAlpha", 1.0f); + } else { + _animVars.set("ikOverlayAlpha", 0.0f); + } } _lastFront = front; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index d26be83d36..cf96c6dc16 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -208,6 +208,8 @@ public: void computeAvatarBoundingCapsule(const FBXGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const; + void setEnableInverseKinematics(bool enable); + protected: bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); } void updateAnimationStateHandlers(); @@ -290,6 +292,8 @@ public: std::map _origRoleAnimations; std::vector _prefetchedAnimations; + bool _enableInverseKinematics { true }; + private: QMap _stateHandlers; int _nextStateHandlerId { 0 };