From 4b5d5062b151688f1c8d5aaf8125addf5e21bd47 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 17 Jun 2019 14:50:19 -0700 Subject: [PATCH] Improvement to Developer > Avatar > Draw Animation This feature was added to help diagnose the root cause of a rarely occurring finger twitching bug. Also, some asserts were added to AnimClip to also help catch a rarely occurring crash. --- interface/src/avatar/MyAvatar.cpp | 27 +++++++++++++++++++++------ interface/src/avatar/MyAvatar.h | 12 +++++++++++- libraries/animation/src/AnimClip.cpp | 22 ++++++++++++++++++++++ libraries/animation/src/AnimNode.h | 2 ++ libraries/animation/src/Rig.cpp | 10 +++++++++- libraries/animation/src/Rig.h | 4 ++-- 6 files changed, 67 insertions(+), 10 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3800a330bb..aba74806c0 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1416,6 +1416,10 @@ void MyAvatar::setEnableDebugDrawAnimPose(bool isEnabled) { } } +void MyAvatar::setDebugDrawAnimPoseName(QString poseName) { + _debugDrawAnimPoseName.set(poseName); +} + void MyAvatar::setEnableDebugDrawPosition(bool isEnabled) { if (isEnabled) { const glm::vec4 red(1.0f, 0.0f, 0.0f, 1.0f); @@ -3086,15 +3090,26 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) { } if (_enableDebugDrawAnimPose && animSkeleton) { - // build absolute AnimPoseVec from rig + AnimPoseVec absPoses; const Rig& rig = _skeletonModel->getRig(); - absPoses.reserve(rig.getJointStateCount()); - for (int i = 0; i < rig.getJointStateCount(); i++) { - absPoses.push_back(AnimPose(rig.getJointTransform(i))); + const glm::vec4 CYAN(0.1f, 0.6f, 0.6f, 1.0f); + + QString name = _debugDrawAnimPoseName.get(); + if (name.isEmpty()) { + // build absolute AnimPoseVec from rig transforms. i.e. the same that are used for rendering. + absPoses.reserve(rig.getJointStateCount()); + for (int i = 0; i < rig.getJointStateCount(); i++) { + absPoses.push_back(AnimPose(rig.getJointTransform(i))); + } + AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarAnimPoses", animSkeleton, absPoses, xform, CYAN); + } else { + AnimNode::ConstPointer node = rig.findAnimNodeByName(name); + if (node) { + rig.buildAbsoluteRigPoses(node->getPoses(), absPoses); + AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarAnimPoses", animSkeleton, absPoses, xform, CYAN); + } } - glm::vec4 cyan(0.1f, 0.6f, 0.6f, 1.0f); - AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarAnimPoses", animSkeleton, absPoses, xform, cyan); } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d60c023caa..f31e6e524e 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -2018,12 +2018,20 @@ public slots: void setEnableDebugDrawDefaultPose(bool isEnabled); /**jsdoc - * Displays animation debug graphics. + * Displays animation debug graphics. By default it shows the animation poses used for rendering. + * However, the property MyAvatar.setDebugDrawAnimPoseName can be used to draw a specific animation node. * @function MyAvatar.setEnableDebugDrawAnimPose * @param {boolean} enabled - true to show the debug graphics, false to hide. */ void setEnableDebugDrawAnimPose(bool isEnabled); + /**jsdoc + * If set it determines which animation debug graphics to draw, when MyAvatar.setEnableDebugDrawAnimPose is set to true. + * @function MyAvatar.setDebugDrawAnimPoseName + * @param {boolean} enabled - true to show the debug graphics, false to hide. + */ + void setDebugDrawAnimPoseName(QString poseName); + /**jsdoc * Displays position debug graphics. * @function MyAvatar.setEnableDebugDrawPosition @@ -2666,6 +2674,8 @@ private: bool _enableDebugDrawIKChains { false }; bool _enableDebugDrawDetailedCollision { false }; + ThreadSafeValueCache _debugDrawAnimPoseName; + mutable bool _cauterizationNeedsUpdate { false }; // do we need to scan children and update their "cauterized" state? AudioListenerMode _audioListenerMode; diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 1a922e507d..84a7e57895 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -107,6 +107,17 @@ static std::vector buildJointIndexMap(const AnimSkeleton& dstSkeleton, cons return jointIndexMap; } +#ifdef USE_CUSTOM_ASSERT +#undef ASSERT +#define ASSERT(x) \ + do { \ + if (!(x)) { \ + int* bad_ptr = 0; \ + *bad_ptr = 0x0badf00d; \ + } \ + } while (0) +#endif // #ifdef USE_CUSTOM_ASSERT + void AnimClip::copyFromNetworkAnim() { assert(_networkAnim && _networkAnim->isLoaded() && _skeleton); _anim.clear(); @@ -165,11 +176,14 @@ void AnimClip::copyFromNetworkAnim() { for (int frame = 0; frame < animFrameCount; frame++) { const HFMAnimationFrame& animFrame = animModel.animationFrames[frame]; + ASSERT(frame >= 0 && frame < animModel.animationFrames.size()); // extract the full rotations from the animFrame (including pre and post rotations from the animModel). std::vector animRotations; animRotations.reserve(animJointCount); for (int i = 0; i < animJointCount; i++) { + ASSERT(i >= 0 && i < animModel.joints.size()); + ASSERT(i >= 0 && i < animFrame.rotations.size()); animRotations.push_back(animModel.joints[i].preRotation * animFrame.rotations[i] * animModel.joints[i].postRotation); } @@ -180,10 +194,12 @@ void AnimClip::copyFromNetworkAnim() { std::vector avatarRotations; avatarRotations.reserve(avatarJointCount); for (int avatarJointIndex = 0; avatarJointIndex < avatarJointCount; avatarJointIndex++) { + ASSERT(avatarJointIndex >= 0 && avatarJointIndex < avatarToAnimJointIndexMap.size()); int animJointIndex = avatarToAnimJointIndexMap[avatarJointIndex]; if (animJointIndex >= 0) { // This joint is in both animation and avatar. // Set the absolute rotation directly + ASSERT(animJointIndex >= 0 && animJointIndex < animRotations.size()); avatarRotations.push_back(animRotations[animJointIndex]); } else { // This joint is NOT in the animation at all. @@ -192,6 +208,7 @@ void AnimClip::copyFromNetworkAnim() { glm::quat avatarParentAbsoluteRot; int avatarParentJointIndex = avatarSkeleton->getParentIndex(avatarJointIndex); if (avatarParentJointIndex >= 0) { + ASSERT(avatarParentJointIndex >= 0 && avatarParentJointIndex < avatarRotations.size()); avatarParentAbsoluteRot = avatarRotations[avatarParentJointIndex]; } avatarRotations.push_back(avatarParentAbsoluteRot * avatarRelativeDefaultRot); @@ -201,6 +218,7 @@ void AnimClip::copyFromNetworkAnim() { // convert avatar rotations into relative frame avatarSkeleton->convertAbsoluteRotationsToRelative(avatarRotations); + ASSERT(frame >= 0 && frame < _anim.size()); _anim[frame].reserve(avatarJointCount); for (int avatarJointIndex = 0; avatarJointIndex < avatarJointCount; avatarJointIndex++) { const AnimPose& avatarDefaultPose = avatarSkeleton->getRelativeDefaultPose(avatarJointIndex); @@ -209,12 +227,15 @@ void AnimClip::copyFromNetworkAnim() { glm::vec3 relativeScale = avatarDefaultPose.scale(); glm::vec3 relativeTranslation; + ASSERT(avatarJointIndex >= 0 && avatarJointIndex < avatarToAnimJointIndexMap.size()); int animJointIndex = avatarToAnimJointIndexMap[avatarJointIndex]; if (animJointIndex >= 0) { // This joint is in both animation and avatar. + ASSERT(animJointIndex >= 0 && animJointIndex < animFrame.translations.size()); const glm::vec3& animTrans = animFrame.translations[animJointIndex]; // retarget translation from animation to avatar + ASSERT(animJointIndex >= 0 && animJointIndex < animModel.animationFrames[0].translations.size()); const glm::vec3& animZeroTrans = animModel.animationFrames[0].translations[animJointIndex]; relativeTranslation = avatarDefaultPose.trans() + boneLengthScale * (animTrans - animZeroTrans); } else { @@ -224,6 +245,7 @@ void AnimClip::copyFromNetworkAnim() { } // build the final pose + ASSERT(avatarJointIndex >= 0 && avatarJointIndex < avatarRotations.size()); _anim[frame].push_back(AnimPose(relativeScale, avatarRotations[avatarJointIndex], relativeTranslation)); } } diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index 31e10ca2d5..ec5f2b7375 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -98,6 +98,8 @@ public: return result; } + const AnimPoseVec& getPoses() const { return getPosesInternal(); } + protected: virtual void setCurrentFrameInternal(float frame) {} diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 633a505d14..a58b8745d7 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2232,6 +2232,14 @@ void Rig::initAnimGraph(const QUrl& url) { } } +AnimNode::ConstPointer Rig::findAnimNodeByName(const QString& name) const { + if (_animNode) { + return _animNode->findByName(name); + } else { + return nullptr; + } +} + bool Rig::getModelRegistrationPoint(glm::vec3& modelRegistrationPointOut) const { if (_animSkeleton && _rootJointIndex >= 0) { modelRegistrationPointOut = _geometryOffset * -_animSkeleton->getAbsoluteDefaultPose(_rootJointIndex).trans(); @@ -2258,7 +2266,7 @@ void Rig::applyOverridePoses() { } } -void Rig::buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut) { +void Rig::buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut) const { DETAILED_PERFORMANCE_TIMER("buildAbsolute"); if (!_animSkeleton) { return; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 786d14200e..02fa965f64 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -196,6 +196,7 @@ public: void initAnimGraph(const QUrl& url); AnimNode::ConstPointer getAnimNode() const { return _animNode; } + AnimNode::ConstPointer findAnimNodeByName(const QString& name) const; AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; } QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList); void removeAnimationStateHandler(QScriptValue handler); @@ -243,7 +244,7 @@ public: Flow& getFlow() { return _internalFlow; } float getUnscaledEyeHeight() const; - + void buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut) const; signals: void onLoadComplete(); @@ -252,7 +253,6 @@ protected: bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); } void updateAnimationStateHandlers(); void applyOverridePoses(); - void buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut); void updateHead(bool headEnabled, bool hipsEnabled, const AnimPose& headMatrix); void updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated,